1:  // ------------------------------------------------------------------------------
   2:  // Copyright (c) Microsoft Corporation. All rights reserved.
   3:  // ------------------------------------------------------------------------------
   4:   
   5:  using System;
   6:  using System.Collections;
   7:  using System.Collections.Generic;
   8:  using System.Drawing;
   9:  using System.Drawing.Imaging;
  10:   
  11:  namespace Microsoft.Protocols.TestTools.StackSdk.RemoteDesktop.Rdprfx
  12:  {
  13:      /// <summary>
  14:      /// The RemoteFX encoder
  15:      /// </summary>
  16:      public class RemoteFXEncoder
  17:      {
  18:          protected const int DWT_PREC = 4;
  19:          protected const int TileSize = 64;
  20:   
  21:   
  22:          #region public Methods
  23:   
  24:          /// <summary>
  25:          /// Encode a tile from a image.
  26:          /// </summary>
  27:          /// <param name="image">The input image.</param>
  28:          /// <param name="leftOffset">The left offset of the tile.</param>
  29:          /// <param name="topOffset">The top offset of the tile.</param>
  30:          /// <param name="encodingContext">The encoding context.</param>
  31:          public static void EncodeTile(Image image, int leftOffset, int topOffset, RemoteFXCodecContext encodingContext)
  32:          {
  33:   
  34:              //Initialize the encoding context
  35:              GetTileData(image, leftOffset, topOffset, encodingContext);
  36:   
  37:              //Do color conversion
  38:              RGBToYCbCr(encodingContext);
  39:   
  40:              //Do three level DWT
  41:              DWT(encodingContext);
  42:   
  43:              //Do quantiztion
  44:              Quantization(encodingContext);
  45:   
  46:              //Do linearization
  47:              Linearization(encodingContext);
  48:   
  49:              //ALRG encode
  50:              RLGREncode(encodingContext);
  51:          }
  52:   
  53:          /// <summary>
  54:          /// Encode a tile from a image.
  55:          /// </summary>
  56:          /// <param name="image">The input RgbTile.</param>
  57:          /// <param name="leftOffset">The left offset of the tile.</param>
  58:          /// <param name="topOffset">The top offset of the tile.</param>
  59:          /// <param name="encodingContext">The encoding context.</param>
  60:          public static void EncodeTile(RgbTile tile, int leftOffset, int topOffset, RemoteFXCodecContext encodingContext)
  61:          {
  62:   
  63:              //Initialize the encoding context
  64:              encodingContext.RSet = tile.RSet;
  65:              encodingContext.GSet = tile.GSet;
  66:              encodingContext.BSet = tile.BSet;
  67:   
  68:              //Do color conversion
  69:              RGBToYCbCr(encodingContext);
  70:   
  71:              //Do three level DWT
  72:              DWT(encodingContext);
  73:   
  74:              //Do quantiztion
  75:              Quantization(encodingContext);
  76:   
  77:              //Do linearization
  78:              Linearization(encodingContext);
  79:   
  80:              //ALRG encode
  81:              RLGREncode(encodingContext);
  82:          }
  83:   
  84:          //Load a tile from image
  85:          public static void GetTileData(Image image, int leftOffset, int topOffset, RemoteFXCodecContext encodingContext)
  86:          {
  87:              Bitmap bitmap = new Bitmap(image);
  88:              encodingContext.RSet = new byte[TileSize, TileSize];
  89:              encodingContext.GSet = new byte[TileSize, TileSize];
  90:              encodingContext.BSet = new byte[TileSize, TileSize];
  91:   
  92:              int right = Math.Min(image.Width - 1, leftOffset + TileSize - 1);
  93:              int bottom = Math.Min(image.Height - 1, topOffset + TileSize - 1);
  94:   
  95:              BitmapData bmpData = bitmap.LockBits(new Rectangle(leftOffset, topOffset, right - leftOffset + 1, bottom - topOffset + 1), ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
  96:   
  97:              unsafe
  98:              {
  99:                  byte* cusor = (byte*)bmpData.Scan0.ToPointer();
 100:                  for (int y = topOffset; y <= bottom; y++)
 101:                  {
 102:                      for (int x = leftOffset; x <= right; x++)
 103:                      {
 104:                          int tileX = x - leftOffset;
 105:                          int tileY = y - topOffset;
 106:                          encodingContext.BSet[tileX, tileY] = cusor[0];
 107:                          encodingContext.GSet[tileX, tileY] = cusor[1];
 108:                          encodingContext.RSet[tileX, tileY] = cusor[2];
 109:                          cusor += 3;
 110:                      }
 111:                      cusor += (bmpData.Stride - 3 * (bmpData.Width));
 112:                  }
 113:              }
 114:              bitmap.UnlockBits(bmpData);
 115:          }
 116:   
 117:          //Color coversion
 118:          public static void RGBToYCbCr(RemoteFXCodecContext encodingContext)
 119:          {
 120:              encodingContext.YSet = new short[TileSize, TileSize];
 121:              encodingContext.CbSet = new short[TileSize, TileSize];
 122:              encodingContext.CrSet = new short[TileSize, TileSize];
 123:              for (int x = 0; x < TileSize; x++)
 124:              {
 125:                  for (int y = 0; y < TileSize; y++)
 126:                  {
 127:                      short[] yuv = RGBToYCbCr(encodingContext.RSet[x, y], encodingContext.GSet[x, y], encodingContext.BSet[x, y]);
 128:                      encodingContext.YSet[x, y] = yuv[0];
 129:                      encodingContext.CbSet[x, y] = yuv[1];
 130:                      encodingContext.CrSet[x, y] = yuv[2];
 131:                  }
 132:              }
 133:          }
 134:   
 135:          //DWT
 136:          public static void DWT(RemoteFXCodecContext encodingContext)
 137:          {
 138:              DWT_RomoteFX(encodingContext.YSet);
 139:              DWT_RomoteFX(encodingContext.CbSet);
 140:              DWT_RomoteFX(encodingContext.CrSet);
 141:          }
 142:   
 143:          //Quantization
 144:          public static void Quantization(RemoteFXCodecContext encodingContext)
 145:          {
 146:              doQuantization_Component(encodingContext.YSet, encodingContext.CodecQuantVals[encodingContext.QuantIdxY]);
 147:              doQuantization_Component(encodingContext.CbSet, encodingContext.CodecQuantVals[encodingContext.QuantIdxCb]);
 148:              doQuantization_Component(encodingContext.CrSet, encodingContext.CodecQuantVals[encodingContext.QuantIdxCr]);
 149:          }
 150:   
 151:          //Linearization
 152:          public static void Linearization(RemoteFXCodecContext encodingContext)
 153:          {
 154:              linearization_Compontent(encodingContext.YSet, out encodingContext.YComponent);
 155:              linearization_Compontent(encodingContext.CbSet, out encodingContext.CbComponent);
 156:              linearization_Compontent(encodingContext.CrSet, out encodingContext.CrComponent);
 157:          }
 158:   
 159:          //RLGREncode
 160:          public static void RLGREncode(RemoteFXCodecContext encodingContext)
 161:          {
 162:              RLGREncoder encoder = new RLGREncoder();
 163:              encodingContext.YData = encoder.Encode(encodingContext.YComponent, encodingContext.Mode);
 164:              encodingContext.CbData = encoder.Encode(encodingContext.CbComponent, encodingContext.Mode);
 165:              encodingContext.CrData = encoder.Encode(encodingContext.CrComponent, encodingContext.Mode);
 166:          }
 167:   
 168:   
 169:          public static void getColFrom2DArr<T>(T[,] input2D, out T[] output1D, int xIdx, int len)
 170:          {
 171:              output1D = new T[len];
 172:              for (int i = 0; i < len; i++)
 173:              {
 174:                  output1D[i] = input2D[xIdx, i];
 175:              }
 176:          }
 177:   
 178:          public static void getRowFrom2DArr<T>(T[,] input2D, out T[] output1D, int yIdx, int len)
 179:          {
 180:              output1D = new T[len];
 181:              for (int i = 0; i < len; i++)
 182:              {
 183:                  output1D[i] = input2D[i, yIdx];
 184:              }
 185:          }
 186:   
 187:          #endregion
 188:   
 189:          #region Private Methods
 190:   
 191:   
 192:   
 193:          protected static short[] RGBToYCbCr(byte r, byte g, byte b)
 194:          {
 195:              short[] yuv = new short[3];
 196:   
 197:              float rF = r / 255.0f;
 198:              float gF = g / 255.0f;
 199:              float bF = b / 255.0f;
 200:   
 201:              float y = 0.299f * rF + 0.587f * gF + 0.114f * bF - 0.5f;
 202:              float u = -0.168935f * rF - 0.331665f * gF + 0.50059f * bF;// + 0.5f;
 203:              float v = 0.499813f * rF - 0.418531f * gF - 0.081282f * bF;// + 0.5f;
 204:   
 205:              //* 16 == << 4: with 4 fractional bits
 206:              // yuv[0] = (short)(y * 255.0f * 16);
 207:              // yuv[1] = (short)(u * 255.0f * 16);
 208:              // yuv[2] = (short)(v * 255.0f * 16);
 209:   
 210:              yuv[0] = (short)(y * 255.0f);
 211:              yuv[1] = (short)(u * 255.0f);
 212:              yuv[2] = (short)(v * 255.0f);
 213:   
 214:              return yuv;
 215:          }
 216:   
 217:          protected static void DWT_RomoteFX(short[,] data2D)
 218:          {
 219:              DWT_2D(data2D, 1);//level 1
 220:              DWT_2D(data2D, 2);//level 2
 221:              DWT_2D(data2D, 3);//level 3
 222:          }
 223:   
 224:          /// <summary>
 225:          /// Quantization 
 226:          /// </summary>
 227:          /// <param name="component">Y_Set, Cb_Set, Cr_Set</param>
 228:          /// <param name="tsRfxCodecQuant">TS_RFX_CODEC_QUANT struct stored quantization factor</param>
 229:          protected static void doQuantization_Component(short[,] component, TS_RFX_CODEC_QUANT tsRfxCodecQuant)
 230:          {
 231:              // Quantization factor: HL1, LH1, HH1, HL2, LH2, HH2, HL3, LH3, HH3, LL3            
 232:              Hashtable scaleValueTable = new Hashtable();
 233:              int HL1_Factor = tsRfxCodecQuant.HL1_HH1 & 0x0f;
 234:              int LH1_Factor = (tsRfxCodecQuant.HH2_LH1 & 0xf0) >> 4;
 235:              int HH1_Factor = (tsRfxCodecQuant.HL1_HH1 & 0xf0) >> 4;
 236:              int HL2_Factor = (tsRfxCodecQuant.LH2_HL2 & 0xf0) >> 4;
 237:              int LH2_Factor = tsRfxCodecQuant.LH2_HL2 & 0x0f;
 238:              int HH2_Factor = tsRfxCodecQuant.HH2_LH1 & 0x0f;
 239:              int HL3_Factor = tsRfxCodecQuant.HL3_HH3 & 0x0f;
 240:              int LH3_Factor = (tsRfxCodecQuant.LL3_LH3 & 0xf0) >> 4;
 241:              int HH3_Factor = (tsRfxCodecQuant.HL3_HH3 & 0xf0) >> 4;
 242:              int LL3_Factor = tsRfxCodecQuant.LL3_LH3 & 0x0f;
 243:              int[] HL_Factor = { HL1_Factor, HL2_Factor, HL3_Factor };
 244:              int[] LH_Factor = { LH1_Factor, LH2_Factor, LH3_Factor };
 245:              int[] HH_Factor = { HH1_Factor, HH2_Factor, HH3_Factor };
 246:   
 247:              int top, left, right, bottom;
 248:   
 249:              //Level 1, 2, 3
 250:              for (int i = 0; i <= 2; i++)
 251:              {
 252:                  int levelSize = TileSize >> i;
 253:   
 254:                  //HL1,2,3
 255:                  top = 0;
 256:                  left = levelSize / 2;
 257:                  right = levelSize - 1;
 258:                  bottom = levelSize / 2 - 1;
 259:                  doQuantization_Subband(component, left, top, right, bottom, HL_Factor[i]);
 260:   
 261:                  //LH1,2,3
 262:                  top = levelSize / 2;
 263:                  left = 0;
 264:                  right = levelSize / 2 - 1;
 265:                  bottom = levelSize - 1;
 266:                  doQuantization_Subband(component, left, top, right, bottom, LH_Factor[i]);
 267:   
 268:                  //HH1,2,3
 269:                  top = levelSize / 2;
 270:                  left = levelSize / 2;
 271:                  right = levelSize - 1;
 272:                  bottom = levelSize - 1;
 273:                  doQuantization_Subband(component, left, top, right, bottom, HH_Factor[i]);
 274:              }
 275:              //LL3
 276:              top = 0;
 277:              left = 0;
 278:              right = TileSize / 8 - 1;
 279:              bottom = TileSize / 8 - 1;
 280:              doQuantization_Subband(component, left, top, right, bottom, LL3_Factor);
 281:          }
 282:   
 283:          private static short quant(short input, int factor)
 284:          {
 285:              short output;
 286:              // output = (short)(Math.Abs(input) >> ((factor - 6) + 4));
 287:              output = (short)(Math.Abs(input) >> (factor - 6));
 288:              if (input < 0) output *= -1;
 289:              return output;
 290:          }
 291:   
 292:          private static void doQuantization_Subband(short[,] input, int left, int top, int right, int bottom, int factor)
 293:          {
 294:              for (int x = left; x <= right; x++)
 295:              {
 296:                  for (int y = top; y <= bottom; y++)
 297:                  {
 298:                      input[x, y] = quant(input[x, y], factor);
 299:                  }
 300:              }
 301:          }
 302:   
 303:          protected static void linearization_Compontent(short[,] compontent, out short[] lineOutput)
 304:          {
 305:              //sequence: HL1, LH1, HH1, HL2, LH2, HH2, HL3, LH3, HH3, and LL3
 306:              lineOutput = new short[TileSize * TileSize];
 307:              int curIdx = 0;
 308:   
 309:              int top, left, right, bottom;
 310:              short[] bandOutput;
 311:   
 312:              for (int i = 0; i <= 2; i++)
 313:              {
 314:                  int levelSize = TileSize >> i;
 315:   
 316:                  //HL
 317:                  top = 0;
 318:                  left = levelSize / 2;
 319:                  right = levelSize - 1;
 320:                  bottom = levelSize / 2 - 1;
 321:                  linearization_SubBand(compontent, left, top, right, bottom, out bandOutput);
 322:                  Array.Copy(bandOutput, 0, lineOutput, curIdx, bandOutput.Length);
 323:                  curIdx += bandOutput.Length;
 324:   
 325:                  //LH
 326:                  top = levelSize / 2;
 327:                  left = 0;
 328:                  right = levelSize / 2 - 1;
 329:                  bottom = levelSize - 1;
 330:                  linearization_SubBand(compontent, left, top, right, bottom, out bandOutput);
 331:                  Array.Copy(bandOutput, 0, lineOutput, curIdx, bandOutput.Length);
 332:                  curIdx += bandOutput.Length;
 333:   
 334:                  //HH
 335:                  top = levelSize / 2;
 336:                  left = levelSize / 2;
 337:                  right = levelSize - 1;
 338:                  bottom = levelSize - 1;
 339:                  linearization_SubBand(compontent, left, top, right, bottom, out bandOutput);
 340:                  Array.Copy(bandOutput, 0, lineOutput, curIdx, bandOutput.Length);
 341:                  curIdx += bandOutput.Length;
 342:              }
 343:              //LL
 344:              top = 0;
 345:              left = 0;
 346:              right = TileSize / 8 - 1;
 347:              bottom = TileSize / 8 - 1;
 348:              linearization_SubBand(compontent, left, top, right, bottom, out bandOutput);
 349:              for (int i = bandOutput.Length - 1; i > 0; i--)
 350:              {
 351:                  bandOutput[i] = (short)(bandOutput[i] - bandOutput[i - 1]);
 352:              }
 353:   
 354:              Array.Copy(bandOutput, 0, lineOutput, curIdx, bandOutput.Length);
 355:              curIdx += bandOutput.Length;
 356:          }
 357:   
 358:          private static void linearization_SubBand(short[,] input, int left, int top, int right, int bottom, out short[] bandOutput)
 359:          {
 360:              int totalNum = (right - left + 1) * (bottom - top + 1);
 361:              bandOutput = new short[totalNum];
 362:              int curIdx = 0;
 363:              for (int y = top; y <= bottom; y++)
 364:              {
 365:                  for (int x = left; x <= right; x++)
 366:                  {
 367:                      bandOutput[curIdx++] = input[x, y];
 368:                  }
 369:              }
 370:          }
 371:   
 372:          private static short DWT_H(short[] dataArr, int n)
 373:          {
 374:              //n < dataArr.length/2
 375:              short x2n, x2n1, x2n2;
 376:              if (n == -1)
 377:              {
 378:                  x2n = dataArr[2];
 379:                  x2n1 = dataArr[1];
 380:                  x2n2 = dataArr[0];
 381:              }
 382:              else
 383:              {
 384:                  x2n = dataArr[2 * n];
 385:                  x2n1 = dataArr[2 * n + 1];
 386:                  if (2 * n + 2 >= dataArr.Length - 1)
 387:                  {
 388:                      x2n2 = x2n;
 389:                  }
 390:                  else
 391:                  {
 392:                      x2n2 = dataArr[2 * n + 2];
 393:                  }
 394:              }
 395:   
 396:              short result = (short)((x2n1 - (x2n + x2n2 + 0.0f) / 2) / 2);
 397:              return result;
 398:          }
 399:   
 400:          private static short DWT_L(short[] dataArr, int n)
 401:          {
 402:              short result = (short)(dataArr[2 * n] + (DWT_H(dataArr, n - 1) + DWT_H(dataArr, n)) / 2);
 403:              return result;
 404:          }
 405:   
 406:          private static void DWT(short[] dataArr)
 407:          {
 408:              int N = dataArr.Length / 2;
 409:              short[] hResult = new short[N];
 410:              short[] lResult = new short[N];
 411:              for (int i = 0; i < N; i++)
 412:              {
 413:                  hResult[i] = DWT_H(dataArr, i);
 414:                  lResult[i] = DWT_L(dataArr, i);
 415:              }
 416:              for (int i = 0; i < N; i++)
 417:              {
 418:                  dataArr[i] = lResult[i];
 419:                  dataArr[N + i] = hResult[i];
 420:              }
 421:          }
 422:   
 423:          private static void DWT_2D(short[,] data2D, int level)
 424:          {
 425:              //level > 0
 426:              //data2D.Length % (1<<(level - 1)) == 0
 427:              int inScopelen = TileSize >> (level - 1);
 428:   
 429:              //Vertical DWT
 430:              for (int x = 0; x < inScopelen; x++)
 431:              {
 432:                  short[] col;
 433:                  getColFrom2DArr<short>(data2D, out col, x, inScopelen);
 434:                  DWT(col);
 435:                  for (int y = 0; y < inScopelen; y++)
 436:                  {
 437:                      data2D[x, y] = col[y];
 438:                  }
 439:              }
 440:              //Horizontal DWT
 441:              for (int y = 0; y < inScopelen; y++)
 442:              {
 443:                  short[] row;
 444:                  getRowFrom2DArr<short>(data2D, out row, y, inScopelen);
 445:                  DWT(row);
 446:                  for (int x = 0; x < inScopelen; x++)
 447:                  {
 448:                      data2D[x, y] = row[x];
 449:                  }
 450:              }
 451:          }
 452:          
 453:          private static void Convert2Dto1D<T>(T[,] sourceArr, out T[] targetArr, ArrayDirection direction)
 454:          {
 455:              int len0 = sourceArr.GetLength(0);
 456:              int len1 = sourceArr.GetLength(1);
 457:              int totalLen = len0 * len1;
 458:   
 459:              targetArr = new T[totalLen];
 460:              int idx = 0;
 461:              if (direction == ArrayDirection.Horizontal)
 462:              {
 463:                  for (int y = 0; y < len1; y++)
 464:                  {
 465:                      for (int x = 0; x < len0; x++)
 466:                      {
 467:                          targetArr[idx++] = sourceArr[x, y];
 468:                      }
 469:                  }
 470:              }
 471:              else
 472:              {
 473:                  for (int x = 0; x < len0; x++)
 474:                  {
 475:                      for (int y = 0; y < len1; y++)
 476:                      {
 477:                          targetArr[idx++] = sourceArr[x, y];
 478:                      }
 479:                  }
 480:              }
 481:          }
 482:   
 483:          private static void Convert1Dto2D<T>(T[] sourceArr, int xLen, int yLen, out T[,] targetArr, ArrayDirection direction)
 484:          {
 485:              targetArr = new T[xLen, yLen];
 486:              int curIdx = 0;
 487:              if (direction == ArrayDirection.Horizontal)
 488:              {
 489:                  for (int y = 0; y < yLen; y++)
 490:                  {
 491:                      for (int x = 0; x < xLen; x++)
 492:                      {
 493:                          targetArr[x, y] = sourceArr[curIdx++];
 494:                      }
 495:                  }
 496:              }
 497:              else
 498:              {
 499:                  for (int x = 0; x < xLen; x++)
 500:                  {
 501:                      for (int y = 0; y < yLen; y++)
 502:                      {
 503:                          targetArr[x, y] = sourceArr[curIdx++];
 504:                      }
 505:                  }
 506:              }
 507:          }
 508:   
 509:          private static void RGBToYCbCr(byte[] rSet, byte[] gSet, byte[] bSet, out short[] ySet, out short[] cbSet, out short[] crSet)
 510:          {
 511:              ySet = new short[rSet.Length];
 512:              cbSet = new short[rSet.Length];
 513:              crSet = new short[rSet.Length];
 514:   
 515:              for (int i = 0; i < rSet.Length; i++)
 516:              {
 517:                  float r = rSet[i] / 255.0f;
 518:                  float g = gSet[i] / 255.0f;
 519:                  float b = bSet[i] / 255.0f;
 520:   
 521:                  float y = 0.299f * r + 0.587f * g + 0.114f * b - 0.5f;
 522:                  float u = -0.168935f * r - 0.331665f * g + 0.50059f * b;// + 0.5f;
 523:                  float v = 0.499813f * r - 0.418531f * g - 0.081282f * b;// + 0.5f;
 524:   
 525:                  //<< 4
 526:                  ySet[i] = (short)(y * 255.0f * 16);
 527:                  cbSet[i] = (short)(u * 255.0f * 16);
 528:                  crSet[i] = (short)(v * 255.0f * 16);
 529:              }
 530:          }
 531:          
 532:          #endregion
 533:      }
 534:   
 535:  }