1 // 2 // Copyright (c) 2010-2022 Antmicro 3 // Copyright (c) 2011-2015 Realtime Embedded 4 // Copyright (c) 2020-2021 Microsoft 5 // 6 // This file is licensed under the MIT License. 7 // Full license text is available in 'licenses/MIT.txt'. 8 // 9 using System; 10 using System.Linq.Expressions; 11 using System.Collections.Generic; 12 using System.Collections.Concurrent; 13 using ELFSharp.ELF; 14 15 namespace Antmicro.Renode.Backends.Display 16 { 17 public static class PixelManipulationTools 18 { GetConverter(PixelFormat inputFormat, Endianess inputEndianess, PixelFormat outputFormat, Endianess outputEndianess, PixelFormat? clutInputFormat = null, Pixel inputFixedColor = null )19 public static IPixelConverter GetConverter(PixelFormat inputFormat, Endianess inputEndianess, PixelFormat outputFormat, Endianess outputEndianess, PixelFormat? clutInputFormat = null, Pixel inputFixedColor = null /* fixed color for A4 and A8 mode */) 20 { 21 var converterConfiguration = Tuple.Create(inputFormat, inputEndianess, outputFormat, outputEndianess, clutInputFormat, inputFixedColor); 22 return convertersCache.GetOrAdd(converterConfiguration, (_) => 23 new PixelConverter(inputFormat, outputFormat, GenerateConvertMethod( 24 new BufferDescriptor 25 { 26 ColorFormat = inputFormat, 27 ClutColorFormat = clutInputFormat, 28 FixedColor = inputFixedColor, 29 DataEndianness = inputEndianess 30 }, 31 new BufferDescriptor 32 { 33 ColorFormat = outputFormat, 34 DataEndianness = outputEndianess 35 }))); 36 } 37 GetBlender(PixelFormat backBuffer, Endianess backBufferEndianess, PixelFormat frontBuffer, Endianess frontBufferEndianes, PixelFormat output, Endianess outputEndianess, PixelFormat? clutForegroundFormat = null, PixelFormat? clutBackgroundFormat = null, Pixel bgFixedColor = null, Pixel fgFixedColor = null)38 public static IPixelBlender GetBlender(PixelFormat backBuffer, Endianess backBufferEndianess, PixelFormat frontBuffer, Endianess frontBufferEndianes, PixelFormat output, Endianess outputEndianess, PixelFormat? clutForegroundFormat = null, PixelFormat? clutBackgroundFormat = null, Pixel bgFixedColor = null, Pixel fgFixedColor = null) 39 { 40 var blenderConfiguration = Tuple.Create(backBuffer, backBufferEndianess, frontBuffer, frontBufferEndianes, output, outputEndianess, bgFixedColor, fgFixedColor); 41 return blendersCache.GetOrAdd(blenderConfiguration, (_) => 42 new PixelBlender(backBuffer, frontBuffer, output, GenerateBlendMethod( 43 new BufferDescriptor 44 { 45 ColorFormat = backBuffer, 46 ClutColorFormat = clutBackgroundFormat, 47 FixedColor = bgFixedColor, 48 DataEndianness = backBufferEndianess 49 }, 50 new BufferDescriptor 51 { 52 ColorFormat = frontBuffer, 53 FixedColor = fgFixedColor, 54 ClutColorFormat = clutForegroundFormat, 55 DataEndianness = frontBufferEndianes 56 }, 57 new BufferDescriptor 58 { 59 ColorFormat = output, 60 DataEndianness = outputEndianess 61 }))); 62 } 63 GenerateBlendMethod(BufferDescriptor backgroudBufferDescriptor, BufferDescriptor foregroundBufferDescriptor, BufferDescriptor outputBufferDescriptor)64 private static BlendDelegate GenerateBlendMethod(BufferDescriptor backgroudBufferDescriptor, BufferDescriptor foregroundBufferDescriptor, BufferDescriptor outputBufferDescriptor) 65 { 66 var outputPixel = new PixelDescriptor(); 67 var inputBackgroundPixel = new PixelDescriptor(); 68 var inputForegroundPixel = new PixelDescriptor(); 69 var contantBackgroundPixel = new PixelDescriptor(); 70 71 var vBackPos = Expression.Variable(typeof(int), "backPos"); 72 var vFrontPos = Expression.Variable(typeof(int), "frontPos"); 73 var vOutPos = Expression.Variable(typeof(int), "outPos"); 74 var vBackStep = Expression.Variable(typeof(int), "backStep"); 75 var vFrontStep = Expression.Variable(typeof(int), "frontStep"); 76 var vOutStep = Expression.Variable(typeof(int), "outStep"); 77 var vLength = Expression.Variable(typeof(int), "length"); 78 79 var vBackBuffer = Expression.Parameter(typeof(byte[]), "backBuffer"); 80 var vBackClutBuffer = Expression.Parameter(typeof(byte[]), "backClutBuffer"); 81 var vFrontBuffer = Expression.Parameter(typeof(byte[]), "frontBuffer"); 82 var vFrontClutBuffer = Expression.Parameter(typeof(byte[]), "frontClutBuffer"); 83 var vOutputBuffer = Expression.Parameter(typeof(byte[]).MakeByRefType(), "outputBuffer"); 84 var vBackgroundColor = Expression.Parameter(typeof(Pixel), "backgroundColor"); 85 var vBackBufferAlphaMultiplier = Expression.Parameter(typeof(byte), "backBufferAlphaMultiplier"); 86 var vFrontBufferAlphaMultiplier = Expression.Parameter(typeof(byte), "frontBufferAlphaMultiplier"); 87 88 var vBackgroundBlendingMode = Expression.Parameter(typeof(PixelBlendingMode), "backgroundBlendingMode"); 89 var vForegroundBlendingMode = Expression.Parameter(typeof(PixelBlendingMode), "foregroundBlendingMode"); 90 91 var vBackAlphaBlended = Expression.Variable(typeof(uint), "backAlphaBlended"); 92 var vBackgroundColorAlphaBlended = Expression.Variable(typeof(uint), "backgroundColorAlphaBlended"); 93 94 var tmp = Expression.Variable(typeof(uint)); 95 96 var outOfLoop = Expression.Label(); 97 98 var block = Expression.Block( 99 new[] { outputPixel.RedChannel, outputPixel.GreenChannel, outputPixel.BlueChannel, outputPixel.AlphaChannel, 100 inputForegroundPixel.RedChannel, inputForegroundPixel.GreenChannel, inputForegroundPixel.BlueChannel, inputForegroundPixel.AlphaChannel, 101 inputBackgroundPixel.RedChannel, inputBackgroundPixel.GreenChannel, inputBackgroundPixel.BlueChannel, inputBackgroundPixel.AlphaChannel, 102 vBackStep, vFrontStep, vOutStep, vLength, vBackPos, vFrontPos, vOutPos, 103 contantBackgroundPixel.RedChannel, contantBackgroundPixel.GreenChannel, contantBackgroundPixel.BlueChannel, contantBackgroundPixel.AlphaChannel, 104 vBackAlphaBlended, vBackgroundColorAlphaBlended, 105 tmp 106 }, 107 108 Expression.Assign(vBackStep, Expression.Constant(backgroudBufferDescriptor.ColorFormat.GetColorDepth())), 109 Expression.Assign(vFrontStep, Expression.Constant(foregroundBufferDescriptor.ColorFormat.GetColorDepth())), 110 Expression.Assign(vOutStep, Expression.Constant(outputBufferDescriptor.ColorFormat.GetColorDepth())), 111 Expression.Assign(vLength, Expression.Property(vBackBuffer, "Length")), 112 113 Expression.Assign(vBackPos, Expression.Constant(0x00)), 114 Expression.Assign(vFrontPos, Expression.Constant(0x00)), 115 Expression.Assign(vOutPos, Expression.Constant(0x00)), 116 117 Expression.Assign(contantBackgroundPixel.AlphaChannel, Expression.Convert(Expression.Property(vBackgroundColor, "Alpha"), typeof(uint))), 118 Expression.Assign(contantBackgroundPixel.RedChannel, Expression.Convert(Expression.Property(vBackgroundColor, "Red"), typeof(uint))), 119 Expression.Assign(contantBackgroundPixel.GreenChannel, Expression.Convert(Expression.Property(vBackgroundColor, "Green"), typeof(uint))), 120 Expression.Assign(contantBackgroundPixel.BlueChannel, Expression.Convert(Expression.Property(vBackgroundColor, "Blue"), typeof(uint))), 121 122 Expression.Loop( 123 Expression.IfThenElse(Expression.LessThan(vBackPos, vLength), 124 Expression.Block( 125 126 GenerateFrom(backgroudBufferDescriptor, vBackBuffer, vBackClutBuffer, vBackPos, inputBackgroundPixel, tmp), 127 GenerateFrom(foregroundBufferDescriptor, vFrontBuffer, vFrontClutBuffer, vFrontPos, inputForegroundPixel, tmp), 128 129 Expression.Switch(typeof(void), vBackgroundBlendingMode, null, null, new SwitchCase[] 130 { 131 // Multiply: input BG Pixel Alpha *= bg alpha 132 Expression.SwitchCase( 133 Expression.Assign(inputBackgroundPixel.AlphaChannel, Expression.Divide(Expression.Multiply(inputBackgroundPixel.AlphaChannel, Expression.Convert(vBackBufferAlphaMultiplier, typeof(uint))), Expression.Constant((uint)0xFF))), 134 Expression.Constant(PixelBlendingMode.Multiply) 135 ), 136 137 // Replace: input BG Pixel Alpha = bg alpha 138 Expression.SwitchCase( 139 Expression.Assign(inputBackgroundPixel.AlphaChannel, Expression.Convert(vBackBufferAlphaMultiplier, typeof(uint))), 140 Expression.Constant(PixelBlendingMode.Replace) 141 ) 142 }), 143 144 Expression.Switch(typeof(void), vForegroundBlendingMode, null, null, new SwitchCase[] 145 { 146 // Multiply: input FG Pixel Alpha *= fg alpha 147 Expression.SwitchCase( 148 Expression.Assign(inputForegroundPixel.AlphaChannel, Expression.Divide(Expression.Multiply(inputForegroundPixel.AlphaChannel, Expression.Convert(vFrontBufferAlphaMultiplier, typeof(uint))), Expression.Constant((uint)0xFF))), 149 Expression.Constant(PixelBlendingMode.Multiply) 150 ), 151 152 // Replace: input FG Pixel Alpha = fg alpha 153 Expression.SwitchCase( 154 Expression.Assign(inputForegroundPixel.AlphaChannel, Expression.Convert(vFrontBufferAlphaMultiplier, typeof(uint))), 155 Expression.Constant(PixelBlendingMode.Replace) 156 ) 157 }), 158 159 Expression.Block( 160 // (b_alpha * (0xFF - f_alpha)) / 0xFF 161 Expression.Assign( 162 vBackAlphaBlended, 163 Expression.Divide( 164 Expression.Multiply( 165 inputBackgroundPixel.AlphaChannel, 166 Expression.Subtract( 167 Expression.Constant((uint)0xFF), 168 inputForegroundPixel.AlphaChannel)), 169 Expression.Constant((uint)0xFF))), 170 171 // (c_alpha * (0xFF - (f_alpha + b_alpha * (0xFF - f_alpha)))) / 0xFF 172 Expression.Assign( 173 vBackgroundColorAlphaBlended, 174 Expression.Divide( 175 Expression.Multiply( 176 contantBackgroundPixel.AlphaChannel, 177 Expression.Subtract( 178 Expression.Constant((uint)0xFF), 179 Expression.Add( 180 inputForegroundPixel.AlphaChannel, 181 vBackAlphaBlended))), 182 Expression.Constant((uint)0xFF))), 183 184 Expression.Assign( 185 outputPixel.AlphaChannel, 186 Expression.Add( 187 inputForegroundPixel.AlphaChannel, 188 Expression.Add( 189 vBackAlphaBlended, 190 vBackgroundColorAlphaBlended))), 191 192 Expression.IfThenElse( 193 // If output pixel is 100% transparent 194 Expression.Equal(outputPixel.AlphaChannel, Expression.Constant((uint)0)), 195 // ... then the outputpixel should actually be equal to the underlying background pixel 196 Expression.Block( 197 Expression.Assign(outputPixel.RedChannel, contantBackgroundPixel.RedChannel), 198 Expression.Assign(outputPixel.GreenChannel, contantBackgroundPixel.GreenChannel), 199 Expression.Assign(outputPixel.BlueChannel, contantBackgroundPixel.BlueChannel), 200 Expression.Assign(outputPixel.AlphaChannel, contantBackgroundPixel.AlphaChannel)), 201 // ... else, apply alpha to all color channels 202 Expression.Block( 203 Expression.Assign( 204 outputPixel.RedChannel, 205 Expression.Divide( 206 Expression.Add( 207 Expression.Add( 208 Expression.Multiply(vBackgroundColorAlphaBlended, contantBackgroundPixel.RedChannel), 209 Expression.Multiply(vBackAlphaBlended, inputBackgroundPixel.RedChannel)), 210 Expression.Multiply(inputForegroundPixel.AlphaChannel, inputForegroundPixel.RedChannel)), 211 outputPixel.AlphaChannel)), 212 Expression.Assign( 213 outputPixel.GreenChannel, 214 Expression.Divide( 215 Expression.Add( 216 Expression.Add( 217 Expression.Multiply(vBackgroundColorAlphaBlended, contantBackgroundPixel.GreenChannel), 218 Expression.Multiply(vBackAlphaBlended, inputBackgroundPixel.GreenChannel)), 219 Expression.Multiply(inputForegroundPixel.AlphaChannel, inputForegroundPixel.GreenChannel)), 220 outputPixel.AlphaChannel)), 221 Expression.Assign( 222 outputPixel.BlueChannel, 223 Expression.Divide( 224 Expression.Add( 225 Expression.Add( 226 Expression.Multiply(vBackgroundColorAlphaBlended, contantBackgroundPixel.BlueChannel), 227 Expression.Multiply(vBackAlphaBlended, inputBackgroundPixel.BlueChannel)), 228 Expression.Multiply(inputForegroundPixel.AlphaChannel, inputForegroundPixel.BlueChannel)), 229 outputPixel.AlphaChannel)))) 230 ), 231 232 GenerateTo(outputBufferDescriptor, vOutputBuffer, vOutPos, outputPixel), 233 234 Expression.AddAssign(vBackPos, vBackStep), 235 Expression.AddAssign(vFrontPos, vFrontStep), 236 Expression.AddAssign(vOutPos, vOutStep) 237 ), 238 Expression.Break(outOfLoop) 239 ), 240 outOfLoop 241 ) 242 ); 243 244 return Expression.Lambda<BlendDelegate>(block, vBackBuffer, vBackClutBuffer, vFrontBuffer, vFrontClutBuffer, vOutputBuffer, vBackgroundColor, vBackBufferAlphaMultiplier, vBackgroundBlendingMode, vFrontBufferAlphaMultiplier, vForegroundBlendingMode).Compile(); 245 } 246 GenerateConvertMethod(BufferDescriptor inputBufferDescriptor, BufferDescriptor outputBufferDescriptor)247 private static ConvertDelegate GenerateConvertMethod(BufferDescriptor inputBufferDescriptor, BufferDescriptor outputBufferDescriptor) 248 { 249 var vColor = new PixelDescriptor(); 250 251 var vInStep = Expression.Variable(typeof(int), "inStep"); 252 var vOutStep = Expression.Variable(typeof(int), "outStep"); 253 var vLength = Expression.Variable(typeof(int), "length"); 254 255 var vInputBuffer = Expression.Parameter(typeof(byte[]), "inputBuffer"); 256 var vClutBuffer = Expression.Parameter(typeof(byte[]), "clutBuffer"); 257 var vAlpha = Expression.Parameter(typeof(byte), "alpha"); 258 var vAlphaReplaceMode = Expression.Parameter(typeof(PixelBlendingMode), "alphaReplaceMode"); 259 var vOutputBuffer = Expression.Parameter(typeof(byte[]).MakeByRefType(), "outputBuffer"); 260 261 var vInPos = Expression.Variable(typeof(int), "inPos"); 262 var vOutPos = Expression.Variable(typeof(int), "outPos"); 263 264 var tmp = Expression.Variable(typeof(uint)); 265 266 var outOfLoop = Expression.Label(); 267 268 var block = Expression.Block( 269 new [] { vColor.RedChannel, vColor.GreenChannel, vColor.BlueChannel, vColor.AlphaChannel, vInStep, vOutStep, vLength, vInPos, vOutPos, tmp }, 270 271 Expression.Assign(vInStep, Expression.Constant(inputBufferDescriptor.ColorFormat.GetColorDepth())), 272 Expression.Assign(vOutStep, Expression.Constant(outputBufferDescriptor.ColorFormat.GetColorDepth())), 273 Expression.Assign(vLength, Expression.Property(vInputBuffer, "Length")), 274 275 Expression.Assign(vInPos, Expression.Constant(0)), 276 Expression.Assign(vOutPos, Expression.Constant(0)), 277 Expression.Loop( 278 Expression.IfThenElse(Expression.LessThan(vInPos, vLength), 279 Expression.Block( 280 GenerateFrom(inputBufferDescriptor, vInputBuffer, vClutBuffer, vInPos, vColor, tmp), 281 282 // handle the case where alpha needs to be changed 283 Expression.Switch(typeof(void), vAlphaReplaceMode, null, null, new SwitchCase[] 284 { 285 // Multiply 286 Expression.SwitchCase( 287 Expression.Assign(vColor.AlphaChannel, Expression.Divide(Expression.Multiply(vColor.AlphaChannel, Expression.Convert(vAlpha, typeof(uint))), Expression.Constant((uint)0xFF))), 288 Expression.Constant(PixelBlendingMode.Multiply) 289 ), 290 291 // Replace 292 Expression.SwitchCase( 293 Expression.Assign(vColor.AlphaChannel, Expression.Convert(vAlpha, typeof(uint))), 294 Expression.Constant(PixelBlendingMode.Replace) 295 ) 296 }), 297 298 GenerateTo(outputBufferDescriptor, vOutputBuffer, vOutPos, vColor), 299 300 Expression.AddAssign(vInPos, vInStep), 301 Expression.AddAssign(vOutPos, vOutStep) 302 ), 303 Expression.Break(outOfLoop) 304 ), 305 outOfLoop 306 ) 307 ); 308 309 return Expression.Lambda<ConvertDelegate>(block, vInputBuffer, vClutBuffer, vAlpha, vAlphaReplaceMode, vOutputBuffer).Compile(); 310 } 311 312 /// <summary> 313 /// Generates expression reading one pixel encoded in given format from input buffer (with bytes ordered accordingly to endianess) at given position 314 /// and storing each channel in separate variable expression. 315 /// </summary> 316 /// <returns>Generated expression.</returns> 317 /// <param name="inputBufferDescriptor">Object containing information about input buffer: color format and endianness.</param> 318 /// <param name="inBuffer">Input buffer.</param> 319 /// <param name="clutBuffer">Color look-up table buffer.</param> 320 /// <param name="inPosition">Position of pixel in buffer.</param> 321 /// <param name="color">Variable where values of color channels should be stored.</param> GenerateFrom(BufferDescriptor inputBufferDescriptor, ParameterExpression inBuffer, ParameterExpression clutBuffer, Expression inPosition, PixelDescriptor color, Expression tmp)322 private static Expression GenerateFrom(BufferDescriptor inputBufferDescriptor, ParameterExpression inBuffer, ParameterExpression clutBuffer, Expression inPosition, PixelDescriptor color, Expression tmp) 323 { 324 byte currentBit = 0; 325 byte currentByte = 0; 326 bool isAlphaSet = false; 327 328 var expressions = new List<Expression>(); 329 var inputBytes = new ParameterExpression[inputBufferDescriptor.ColorFormat.GetColorDepth()]; 330 for(int i = 0; i < inputBytes.Length; i++) 331 { 332 inputBytes[i] = Expression.Variable(typeof(uint)); 333 334 expressions.Add( 335 Expression.Assign( 336 inputBytes[i], 337 Expression.Convert( 338 Expression.ArrayIndex( 339 inBuffer, 340 Expression.Add( 341 inPosition, 342 Expression.Constant((inputBufferDescriptor.DataEndianness == Endianess.BigEndian) ? i : inputBytes.Length - i - 1))), 343 typeof(uint)))); 344 } 345 346 foreach(var colorDescriptor in inputBufferDescriptor.ColorFormat.GetColorsLengths()) 347 { 348 Expression colorExpression = null; 349 350 foreach(var transformation in ByteInflate(colorDescriptor.Value, currentBit)) 351 { 352 Expression currentExpressionFragment = inputBytes[currentByte]; 353 354 if(transformation.MaskBits != 0xFF) 355 { 356 currentExpressionFragment = Expression.And(currentExpressionFragment, Expression.Constant((uint)transformation.MaskBits)); 357 } 358 359 if(transformation.ShiftBits > 0) 360 { 361 currentExpressionFragment = Expression.RightShift(currentExpressionFragment, Expression.Constant((int)transformation.ShiftBits)); 362 } 363 else if(transformation.ShiftBits < 0) 364 { 365 currentExpressionFragment = Expression.And( 366 Expression.LeftShift(currentExpressionFragment, Expression.Constant((int)(-transformation.ShiftBits))), 367 Expression.Constant((uint)0xFF)); 368 } 369 370 currentBit += transformation.UsedBits; 371 if(currentBit >= 8) 372 { 373 currentBit -= 8; 374 currentByte++; 375 } 376 377 colorExpression = (colorExpression == null) ? currentExpressionFragment : Expression.Or(colorExpression, currentExpressionFragment); 378 } 379 380 if(colorDescriptor.Key == ColorType.X) 381 { 382 continue; 383 } 384 385 // luminance - indirect color indexing using CLUT 386 if(colorDescriptor.Key == ColorType.L) 387 { 388 if(!inputBufferDescriptor.ClutColorFormat.HasValue) 389 { 390 throw new ArgumentException("CLUT mode required but not set"); 391 } 392 393 var clutWidth = Expression.Constant((uint)inputBufferDescriptor.ClutColorFormat.Value.GetColorDepth()); 394 var clutOffset = Expression.Multiply(colorExpression, clutWidth); 395 396 expressions.Add(Expression.Assign(tmp, color.AlphaChannel)); 397 398 // todo: indirect parameters should not be needed here, but we m u s t pass something 399 expressions.Add( 400 GenerateFrom(new BufferDescriptor 401 { 402 ColorFormat = inputBufferDescriptor.ClutColorFormat.Value, 403 DataEndianness = inputBufferDescriptor.DataEndianness 404 }, clutBuffer, clutBuffer, Expression.Convert(clutOffset, typeof(int)), color, tmp)); 405 406 expressions.Add(Expression.Assign(color.AlphaChannel, tmp)); 407 } 408 else 409 { 410 Expression currentColor = null; 411 switch(colorDescriptor.Key) 412 { 413 case ColorType.A: 414 currentColor = color.AlphaChannel; 415 isAlphaSet = true; 416 break; 417 case ColorType.B: 418 currentColor = color.BlueChannel; 419 break; 420 case ColorType.G: 421 currentColor = color.GreenChannel; 422 break; 423 case ColorType.R: 424 currentColor = color.RedChannel; 425 break; 426 } 427 428 if((inputBufferDescriptor.ColorFormat == PixelFormat.A4 || inputBufferDescriptor.ColorFormat == PixelFormat.A8) && inputBufferDescriptor.FixedColor != null) 429 { 430 expressions.Add(Expression.Assign(color.AlphaChannel, colorExpression)); 431 expressions.Add(Expression.Assign(color.RedChannel, Expression.Constant((uint)inputBufferDescriptor.FixedColor.Red))); 432 expressions.Add(Expression.Assign(color.GreenChannel, Expression.Constant((uint)inputBufferDescriptor.FixedColor.Green))); 433 expressions.Add(Expression.Assign(color.BlueChannel, Expression.Constant((uint)inputBufferDescriptor.FixedColor.Blue))); 434 } 435 else 436 { 437 expressions.Add(Expression.Assign(currentColor, colorExpression)); 438 439 // filling lsb '0'-bits with copy of msb pattern 440 var numberOfBits = colorDescriptor.Value; 441 var zeroBits = 8 - numberOfBits; 442 while(zeroBits > 0) 443 { 444 expressions.Add(Expression.OrAssign( 445 currentColor, 446 Expression.RightShift( 447 currentColor, 448 Expression.Constant((int)numberOfBits)))); 449 zeroBits -= numberOfBits; 450 } 451 } 452 } 453 } 454 455 if(!isAlphaSet) 456 { 457 expressions.Add(Expression.Assign(color.AlphaChannel, Expression.Constant((uint)0xFF))); 458 } 459 return Expression.Block(inputBytes, expressions); 460 } 461 462 /// <summary> 463 /// Generates expression converting and storing one pixel in output format to output buffer (ordering bytes accordingly to endianess) at given position using channels values provided by red/green/blue/alpha variables. 464 /// </summary> 465 /// <returns>Generated expression.</returns> 466 /// <param name="outputBufferDescriptor">Object containing information about output buffer: color format and endianness.</param> 467 /// <param name="outBuffer">Output buffer.</param> 468 /// <param name="outPosition">Position of pixel in output buffer.</param> 469 /// <param name="color">Object with variables from which value of color channels should be read.</param> GenerateTo(BufferDescriptor outputBufferDescriptor, ParameterExpression outBuffer, ParameterExpression outPosition, PixelDescriptor color)470 private static Expression GenerateTo(BufferDescriptor outputBufferDescriptor, ParameterExpression outBuffer, ParameterExpression outPosition, PixelDescriptor color) 471 { 472 byte currentBit = 0; 473 byte currentByte = 0; 474 var expressions = new List<Expression>(); 475 476 Expression currentExpression = null; 477 478 foreach(var colorDescriptor in outputBufferDescriptor.ColorFormat.GetColorsLengths()) 479 { 480 Expression colorExpression = null; 481 482 switch(colorDescriptor.Key) 483 { 484 case ColorType.A: 485 colorExpression = color.AlphaChannel; 486 break; 487 case ColorType.B: 488 colorExpression = color.BlueChannel; 489 break; 490 case ColorType.G: 491 colorExpression = color.GreenChannel; 492 break; 493 case ColorType.R: 494 colorExpression = color.RedChannel; 495 break; 496 case ColorType.X: 497 colorExpression = Expression.Constant((uint)0xFF); 498 break; 499 case ColorType.L: 500 throw new ArgumentException("Luminance channel is not allowed in target color format"); 501 } 502 503 foreach(var transformation in ByteSqueezeAndMove(colorDescriptor.Value, currentBit)) 504 { 505 Expression currentExpressionFragment = colorExpression; 506 507 if(transformation.MaskBits != 0xFF) 508 { 509 currentExpressionFragment = Expression.And(currentExpressionFragment, Expression.Constant((uint)transformation.MaskBits)); 510 } 511 512 if(transformation.ShiftBits > 0) 513 { 514 currentExpressionFragment = Expression.RightShift(currentExpressionFragment, Expression.Constant((int)transformation.ShiftBits)); 515 } 516 else if(transformation.ShiftBits < 0) 517 { 518 currentExpressionFragment = Expression.And( 519 Expression.LeftShift(currentExpressionFragment, Expression.Constant((int)(-transformation.ShiftBits))), 520 Expression.Constant((uint)0xFF)); 521 522 } 523 524 currentExpression = (currentExpression == null) ? currentExpressionFragment : Expression.Or(currentExpression, currentExpressionFragment); 525 526 currentBit += transformation.UsedBits; 527 while(currentBit >= 8) 528 { 529 expressions.Add( 530 Expression.Assign( 531 Expression.ArrayAccess(outBuffer, 532 Expression.Add( 533 outPosition, 534 Expression.Constant((outputBufferDescriptor.DataEndianness == Endianess.BigEndian) ? (int) currentByte : (outputBufferDescriptor.ColorFormat.GetColorDepth() - currentByte - 1)))), 535 Expression.Convert(currentExpression, typeof(byte)))); 536 537 currentExpression = null; 538 currentBit -= 8; 539 currentByte++; 540 } 541 } 542 } 543 544 return Expression.Block(expressions); 545 } 546 547 /// <summary> 548 /// Calculates a set of transformations that reduces a byte variable into lower number of bits (by cutting off the least significant bits) 549 /// and shifts them by given offset. 550 /// </summary> 551 /// <returns>Set of byte transformations.</returns> 552 /// <param name="bits">Number of bits of resulting data.</param> 553 /// <param name="offset">Number of bits by which resulting data should be shifted (in right direction).</param> ByteSqueezeAndMove(byte bits, byte offset)554 private static TransformationDescriptor[] ByteSqueezeAndMove(byte bits, byte offset) 555 { 556 if(offset < 0 || offset > 7) 557 { 558 throw new ArgumentOutOfRangeException("offset"); 559 } 560 561 if(bits == 0 || bits > 8) 562 { 563 throw new ArgumentOutOfRangeException("bits"); 564 } 565 566 var result = new List<TransformationDescriptor>(); 567 568 var bitsLeft = bits; 569 var additionalShift = 0; 570 while(bitsLeft > 0) 571 { 572 var currentBits = (byte)Math.Min(8 - offset, bitsLeft); 573 var mask = (byte)(((1 << currentBits) - 1) << (8 - additionalShift - currentBits)); 574 result.Add(new TransformationDescriptor((sbyte)(bitsLeft < bits ? -additionalShift : offset), mask, currentBits)); 575 576 additionalShift += currentBits; 577 bitsLeft -= currentBits; 578 } 579 580 return result.ToArray(); 581 } 582 583 /// <summary> 584 /// Calculates a set of transformations that reads reduced byte variable of size 'bits' shifted by 'bitOffset'. 585 /// </summary> 586 /// <returns>Set of byte transformations.</returns> 587 /// <param name="bits">Number of bits of input data.</param> 588 /// <param name="bitOffset">Number of bits by which input data is shifted.</param> ByteInflate(byte bits, byte bitOffset)589 private static TransformationDescriptor[] ByteInflate(byte bits, byte bitOffset) 590 { 591 if(bits == 0 || bits > 8) 592 { 593 throw new ArgumentOutOfRangeException("bits"); 594 } 595 596 if(bitOffset > 7) 597 { 598 throw new ArgumentOutOfRangeException("bitOffset"); 599 } 600 601 var result = new List<TransformationDescriptor>(); 602 603 var bitsLeft = bits; 604 var additionalShift = 0; 605 606 while(bitsLeft > 0) 607 { 608 var currentBits = (byte)Math.Min(8 - bitOffset, bitsLeft); 609 var mask = ((1 << currentBits) - 1) << (8 - bitOffset - currentBits); 610 var shift = (sbyte)(-bitOffset + additionalShift); 611 612 // optimization 613 if ((mask >> shift) == (0xFF >> shift)) 614 { 615 mask = 0xFF; 616 } 617 618 var descriptor = new TransformationDescriptor(shift, (byte)mask, currentBits); 619 result.Add(descriptor); 620 621 bitOffset = 0; 622 additionalShift += currentBits; 623 bitsLeft -= currentBits; 624 } 625 626 return result.ToArray(); 627 } 628 629 private struct TransformationDescriptor 630 { TransformationDescriptorAntmicro.Renode.Backends.Display.PixelManipulationTools.TransformationDescriptor631 public TransformationDescriptor(sbyte shift, byte mask, byte usedBits) : this() 632 { 633 ShiftBits = shift; 634 MaskBits = mask; 635 UsedBits = usedBits; 636 } 637 638 public sbyte ShiftBits { get; private set; } 639 public byte MaskBits { get; private set; } 640 public byte UsedBits { get; private set; } 641 } 642 643 private class PixelConverter : IPixelConverter 644 { PixelConverter(PixelFormat input, PixelFormat output, ConvertDelegate converter)645 public PixelConverter(PixelFormat input, PixelFormat output, ConvertDelegate converter) 646 { 647 Input = input; 648 Output = output; 649 this.converter = converter; 650 } 651 Convert(byte[] inBuffer, ref byte[] outBuffer)652 public void Convert(byte[] inBuffer, ref byte[] outBuffer) 653 { 654 converter(inBuffer, null, 0xff, PixelBlendingMode.NoModification, ref outBuffer); 655 } 656 Convert(byte[] inBuffer, byte[] clutBuffer, byte alpha, PixelBlendingMode alphaReplaceMode, ref byte[] outBuffer)657 public void Convert(byte[] inBuffer, byte[] clutBuffer, byte alpha, PixelBlendingMode alphaReplaceMode, ref byte[] outBuffer) 658 { 659 converter(inBuffer, clutBuffer, alpha, alphaReplaceMode, ref outBuffer); 660 } 661 662 public PixelFormat Input { get; private set; } 663 public PixelFormat Output { get; private set; } 664 665 private readonly ConvertDelegate converter; 666 } 667 668 private class PixelBlender : IPixelBlender 669 { PixelBlender(PixelFormat back, PixelFormat front, PixelFormat output, BlendDelegate blender)670 public PixelBlender(PixelFormat back, PixelFormat front, PixelFormat output, BlendDelegate blender) 671 { 672 BackBuffer = back; 673 FrontBuffer = front; 674 Output = output; 675 this.blender = blender; 676 } 677 Blend(byte[] backBuffer, byte[] frontBuffer, ref byte[] output, Pixel background = null, byte backBufferAlphaMultiplier = 0xFF, PixelBlendingMode backgroundBlendingMode = PixelBlendingMode.Multiply, byte frontBufferAlphaMultiplayer = 0xFF, PixelBlendingMode foregroundBlendingMode = PixelBlendingMode.Multiply)678 public void Blend(byte[] backBuffer, byte[] frontBuffer, ref byte[] output, Pixel background = null, byte backBufferAlphaMultiplier = 0xFF, PixelBlendingMode backgroundBlendingMode = PixelBlendingMode.Multiply, byte frontBufferAlphaMultiplayer = 0xFF, PixelBlendingMode foregroundBlendingMode = PixelBlendingMode.Multiply) 679 { 680 Blend(backBuffer, null, frontBuffer, null, ref output, background, backBufferAlphaMultiplier, backgroundBlendingMode, frontBufferAlphaMultiplayer, foregroundBlendingMode); 681 } 682 Blend(byte[] backBuffer, byte[] backClutBuffer, byte[] frontBuffer, byte[] frontClutBuffer, ref byte[] output, Pixel background = null, byte backBufferAlphaMultiplier = 0xFF, PixelBlendingMode backgroundBlendingMode = PixelBlendingMode.Multiply, byte frontBufferAlphaMultiplayer = 0xFF, PixelBlendingMode foregroundBlendingMode = PixelBlendingMode.Multiply)683 public void Blend(byte[] backBuffer, byte[] backClutBuffer, byte[] frontBuffer, byte[] frontClutBuffer, ref byte[] output, Pixel background = null, byte backBufferAlphaMultiplier = 0xFF, PixelBlendingMode backgroundBlendingMode = PixelBlendingMode.Multiply, byte frontBufferAlphaMultiplayer = 0xFF, PixelBlendingMode foregroundBlendingMode = PixelBlendingMode.Multiply) 684 { 685 if(background == null) 686 { 687 background = new Pixel(0x00, 0x00, 0x00, 0x00); 688 } 689 blender(backBuffer, backClutBuffer, frontBuffer, frontClutBuffer, ref output, background, backBufferAlphaMultiplier, backgroundBlendingMode, frontBufferAlphaMultiplayer, foregroundBlendingMode); 690 } 691 692 public PixelFormat BackBuffer { get; private set; } 693 public PixelFormat FrontBuffer { get; private set; } 694 public PixelFormat Output { get; private set; } 695 696 private readonly BlendDelegate blender; 697 } 698 699 private static ConcurrentDictionary<Tuple<PixelFormat, Endianess, PixelFormat, Endianess, PixelFormat?, Pixel>, IPixelConverter> convertersCache = new ConcurrentDictionary<Tuple<PixelFormat, Endianess, PixelFormat, Endianess, PixelFormat?, Pixel>, IPixelConverter>(); 700 private static ConcurrentDictionary<Tuple<PixelFormat, Endianess, PixelFormat, Endianess, PixelFormat, Endianess, Pixel, Tuple<Pixel>>, IPixelBlender> blendersCache = new ConcurrentDictionary<Tuple<PixelFormat, Endianess, PixelFormat, Endianess, PixelFormat, Endianess, Pixel, Tuple<Pixel>>, IPixelBlender>(); 701 ConvertDelegate(byte[] inBuffer, byte[] clutBuffer, byte alpha, PixelBlendingMode alphaReplaceMode, ref byte[] outBuffer)702 private delegate void ConvertDelegate(byte[] inBuffer, byte[] clutBuffer, byte alpha, PixelBlendingMode alphaReplaceMode, ref byte[] outBuffer); BlendDelegate(byte[] backBuffer, byte[] backClutBuffer, byte[] frontBuffer, byte[] frontClutBuffer, ref byte[] outBuffer, Pixel background = null, byte backBufferAlphaMulitplier = 0xFF, PixelBlendingMode backgroundBlendingMode = PixelBlendingMode.Multiply, byte frontBufferAlphaMultiplayer = 0xFF, PixelBlendingMode foregroundBlendingMode = PixelBlendingMode.Multiply)703 private delegate void BlendDelegate(byte[] backBuffer, byte[] backClutBuffer, byte[] frontBuffer, byte[] frontClutBuffer, ref byte[] outBuffer, Pixel background = null, byte backBufferAlphaMulitplier = 0xFF, PixelBlendingMode backgroundBlendingMode = PixelBlendingMode.Multiply, byte frontBufferAlphaMultiplayer = 0xFF, PixelBlendingMode foregroundBlendingMode = PixelBlendingMode.Multiply); 704 705 private class PixelDescriptor 706 { PixelDescriptor()707 public PixelDescriptor() 708 { 709 RedChannel = Expression.Variable(typeof(uint), "red"); 710 GreenChannel = Expression.Variable(typeof(uint), "green"); 711 BlueChannel = Expression.Variable(typeof(uint), "blue"); 712 AlphaChannel = Expression.Variable(typeof(uint), "alpha"); 713 } 714 715 public ParameterExpression RedChannel; 716 public ParameterExpression GreenChannel; 717 public ParameterExpression BlueChannel; 718 public ParameterExpression AlphaChannel; 719 } 720 721 private class BufferDescriptor 722 { 723 public PixelFormat ColorFormat { get; set; } 724 public Endianess DataEndianness { get; set; } 725 public PixelFormat? ClutColorFormat { get; set; } 726 public Pixel FixedColor { get; set; } // for A4 and A8 modes 727 } 728 } 729 } 730