SingleColorDxtCompressor.cs 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137
  1. #region Using ステートメント
  2. using Microsoft.Xna.Framework;
  3. using Microsoft.Xna.Framework.Content.Pipeline.Graphics;
  4. using Microsoft.Xna.Framework.Graphics.PackedVector;
  5. #endregion
  6. namespace WpfFontPipeline
  7. {
  8. /// <summary>
  9. /// 単色テクスチャイメージに特化したDXT3テクスチャ圧縮クラス
  10. /// </summary>
  11. /// <remarks>
  12. /// フォントなどの高周波成分を含んだ画像はDXT圧縮するべきではないが、
  13. /// 乗算済みアルファの単色画像であればDXT圧縮ブロックを直接生成することで
  14. /// 劣化が少なく、かつテクスチャメモリ使用量を1/4にすることができる。
  15. /// </remarks>
  16. public static class SingleColorDxtCompressor
  17. {
  18. /// <summary>
  19. /// 指定された単色ビットマップ画像をDXT3テクスチャへ変換する
  20. /// </summary>
  21. /// <param name="source">変換元画像</param>
  22. /// <param name="color0">単色カラー</param>
  23. /// <returns>DXT3圧縮された画像</returns>
  24. public static Dxt3BitmapContent Compress(PixelBitmapContent<Color> source,
  25. Color color0)
  26. {
  27. // DXT3ブロックデータを格納するためのバッファを確保
  28. byte[] outputData = new byte[source.Width * source.Height];
  29. // 単色カラーをBGR565に変換する
  30. ushort packedColor = new Bgr565(color0.ToVector3()).PackedValue;
  31. // 指定された画像を圧縮ブロック単位に処理をする
  32. int outputIndex = 0;
  33. for (int blockY = 0; blockY < source.Height; blockY += 4)
  34. {
  35. for (int blockX = 0; blockX < source.Width; blockX += 4)
  36. {
  37. CompressDxt3Block(source, blockX, blockY, packedColor,
  38. outputData, outputIndex);
  39. outputIndex += 16;
  40. }
  41. }
  42. // DXT3テクスチャの生成と圧縮したブロックデータの設定
  43. var result = new Dxt3BitmapContent(source.Width, source.Height);
  44. result.SetPixelData(outputData);
  45. return result;
  46. }
  47. /// <summary>
  48. /// 圧縮ブロックの処理
  49. /// </summary>
  50. /// <param name="source">元画像</param>
  51. /// <param name="blockX">Xブロック位置</param>
  52. /// <param name="blockY">Yブロック位置</param>
  53. /// <param name="color0">単色カラー</param>
  54. /// <param name="outputData">出力先</param>
  55. /// <param name="outputIndex">出力オフセット</param>
  56. private static void CompressDxt3Block(PixelBitmapContent<Color> source,
  57. int blockX, int blockY, ushort color0, byte[] outputData, int outputIndex)
  58. {
  59. long alphaBits = 0;
  60. int rgbBits = 0;
  61. int pixelCount = 0;
  62. // 4x4ブロック内の処理
  63. for (int y = 0; y < 4; ++y)
  64. {
  65. for (int x = 0; x < 4; ++x)
  66. {
  67. // 元のアルファ値の取得
  68. int value = source.GetPixel(blockX + x, blockY + y).A;
  69. int alpha = 0;
  70. int rgb = 0;
  71. // アルファ値によって、出力値を決定。
  72. // ここでは単純にアルファ値領域を4分割するのではなく、6分割にして
  73. // 1/6、1/2、5/6の非線形の閾値を使っている
  74. if (value < 256 / 6)
  75. {
  76. alpha = 0;
  77. rgb = 1; // c1色 = 0
  78. }
  79. else if (value < 256 * 3 / 6)
  80. {
  81. alpha = 5;
  82. rgb = 3; // c3色 = 1/3(c0) + 2/3(c1) = 85
  83. }
  84. else if (value < 256 * 5 / 6)
  85. {
  86. alpha = 10;
  87. rgb = 2; // c2色 = 1/2(c0) + 1/2(c1) = 127
  88. }
  89. else
  90. {
  91. alpha = 15;
  92. rgb = 0; // c0色 = 255
  93. }
  94. // 計算結果ビットを格納
  95. alphaBits |= (long)alpha << (pixelCount * 4);
  96. rgbBits |= rgb << (pixelCount * 2);
  97. pixelCount++;
  98. }
  99. }
  100. // DXT3ブロック情報の出力
  101. // アルファ 8バイト
  102. for (int i = 0; i < 8; ++i)
  103. {
  104. outputData[outputIndex + i] = (byte)(alphaBits >> (i * 8));
  105. }
  106. // カラー値(c0, c1) 4バイト
  107. // c0
  108. outputData[outputIndex + 8] = (byte)(color0 & 0xff);
  109. outputData[outputIndex + 9] = (byte)((color0 >> 8) & 0xff);
  110. // c1
  111. outputData[outputIndex + 10] = 0x00;
  112. outputData[outputIndex + 11] = 0x00;
  113. // RGB情報 4バイト
  114. for (int i = 0; i < 4; ++i)
  115. {
  116. outputData[outputIndex + 12 + i] = (byte)(rgbBits >> (i * 8));
  117. }
  118. }
  119. }
  120. }