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