SHARE
TWEET

QColorMatrix

ForeverZer0 Feb 19th, 2012 89 Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. using System;
  2. using System.Drawing;
  3. using System.Drawing.Imaging;
  4.  
  5. namespace QColorMatrix
  6. {
  7.         /// <summary>
  8.         /// QColorMatrix
  9.         /// http://www.codeguru.com/Cpp/G-M/gdi/gdi/article.php/c3667
  10.         ///
  11.         /// Extension of the GDI+ struct ColorMatrix.
  12.         /// Adds some member functions so you can actually do something with it.
  13.         /// Use QColorMatrix like ColorMatrix to update the ImmageAttributes class.
  14.         /// Use at your own risk. Comments welcome.
  15.         ///
  16.         /// See: http://www.sgi.com/grafica/matrix/
  17.         /// http://www.sgi.com/software/opengl/advanced98/notes/node182.html
  18.         ///
  19.         /// (c) 2003, Sjaak Priester, Amsterdam.
  20.         /// mailto:sjaak@sjaakpriester.nl
  21.         ///
  22.         /// This C# port from original C++ code is done by:
  23.         /// (C) 2010, Vasian Cepa
  24.         /// http://madebits.com
  25.         /// This port fixes a bug in TransformVector and offers some new convenience methods.
  26.         /// This class is not thread-safe.
  27.         /// </summary>
  28.         public class QColorMatrix
  29.         {
  30.                 #region privateData
  31.  
  32.                 private const int MatrixLength = 5;
  33.                 private float[,] m = new float[MatrixLength, MatrixLength];
  34.                 private const float rad = (float)(Math.PI / 180.0);
  35.  
  36.                 /*
  37.                 QColorMatrix rotates hues while preserving the luminance. In other words: only the color information is modified, not the black-and-white levels. If you would remove the color from an image (by setting the saturation to zero), rotating the hue has no effect.
  38.                 Because the luminance of the brightest red on the computer screen (RGB 255, 0, 0) is way lower than the luminance of the brightest green (RGB 0, 255, 0), the brightest red does not translate into the brightest green, but into a darker green.
  39.                 It all has to do with the 'luminance weights' which are assigned to the R, G, and B elements of the color (see the const's in the top part of QColorMatrix.cpp). If all three luminance weights were equal (1.0), the brightest red would translate to the brightest green after rotation. However, rotating the hue would also mean modifying the luminance.
  40.                 See http://www.graficaobscura.com/matrix/ for some more information on hue rotation.
  41.                 For what it's worth, the Photoshop hue control works similar to QColormatrix in this respect.
  42.                 */
  43.  
  44.                 // The luminance weight factors for the RGB color space.
  45.                 // These values are actually preferable to the better known factors of
  46.                 // Y = 0.30R + 0.59G + 0.11B, the formula which is used in color television technique.
  47.                 public static float lumR = 0.3086f;
  48.                 public static float lumG = 0.6094f;
  49.                 public static float lumB = 0.0820f;
  50.  
  51.                 private static QColorMatrix preHue = new QColorMatrix();
  52.                 private static QColorMatrix postHue = new QColorMatrix();
  53.                 private static bool initialized = false;
  54.  
  55.                 #endregion privateData
  56.  
  57.                 /// <summary>
  58.                 /// gdi+ type
  59.                 /// </summary>
  60.                 public enum MatrixOrder { MatrixOrderPrepend = 0, MatrixOrderAppend = 1 };
  61.  
  62.                 #region ctors
  63.  
  64.                 public QColorMatrix()
  65.                 {
  66.                         Reset();
  67.                 }
  68.  
  69.                 public QColorMatrix(float[,] m)
  70.                 {
  71.                         if (m == null)
  72.                         {
  73.                                 Reset();
  74.                                 return;
  75.                         }
  76.                         Copy(m);
  77.                 }
  78.  
  79.                 public QColorMatrix(float[][] m)
  80.                 {
  81.                         FromJaggedMatrix(m);
  82.                 }
  83.  
  84.                 public QColorMatrix(QColorMatrix qm)
  85.                 {
  86.                         Copy(qm);
  87.                 }
  88.  
  89.                 public QColorMatrix(ColorMatrix cm)
  90.                 {
  91.                         FromColorMatrix(cm);
  92.                 }
  93.  
  94.                 #endregion ctors
  95.  
  96.                 public float[,] Matrix { get { return m; } }
  97.  
  98.                 #region conversions
  99.  
  100.                 public void FromJaggedMatrix(float[][] m)
  101.                 {
  102.                         Reset();
  103.                         if (m == null)
  104.                         {
  105.                                 return;
  106.                         }
  107.                         for (int i = 0; i < m.Length; i++)
  108.                         {
  109.                                 if (m[i] == null)
  110.                                 {
  111.                                         throw new ArgumentException();
  112.                                 }
  113.                                 for (int j = 0; j < m[i].Length; j++)
  114.                                 {
  115.                                         this.m[i, j] = m[i][j];
  116.                                 }
  117.                         }
  118.                 }
  119.  
  120.                 public float[][] ToJaggedMatrix()
  121.                 {
  122.                         float[][] t = new float[MatrixLength][];
  123.                         for (int i = 0; i < t.Length; i++)
  124.                         {
  125.                                 t[i] = new float[MatrixLength];
  126.                                 for (int j = 0; j < t[i].Length; j++)
  127.                                 {
  128.                                         t[i][j] = this.m[i, j];
  129.                                 }
  130.                         }
  131.                         return t;
  132.                 }
  133.  
  134.                 public void FromColorMatrix(ColorMatrix cm)
  135.                 {
  136.                         if (cm == null)
  137.                         {
  138.                                 Reset();
  139.                                 return;
  140.                         }
  141.                         for (int i = 0; i < MatrixLength; i++)
  142.                         {
  143.                                 for (int j = 0; j < MatrixLength; j++)
  144.                                 {
  145.                                         m[i, j] = cm[i, j];
  146.                                 }
  147.                         }
  148.                 }
  149.  
  150.                 public ColorMatrix ToColorMatrix()
  151.                 {
  152.                         ColorMatrix cm = new ColorMatrix();
  153.                         for (int i = 0; i < MatrixLength; i++)
  154.                         {
  155.                                 for (int j = 0; j < MatrixLength; j++)
  156.                                 {
  157.                                         cm[i, j] = m[i, j];
  158.                                 }
  159.                         }
  160.                         return cm;
  161.                 }
  162.  
  163.                 #endregion conversions
  164.  
  165.                 #region core
  166.  
  167.                 /// <summary>
  168.                 /// set to identity matrix
  169.                 /// </summary>
  170.                 public void Reset()
  171.                 {
  172.                         for (int i = 0; i < MatrixLength; i++)
  173.                         {
  174.                                 for (int j = 0; j < MatrixLength; j++)
  175.                                 {
  176.                                         m[i, j] = ((i == j) ? 1.0f : 0.0f);
  177.                                 }
  178.                         }
  179.                 }
  180.  
  181.                 /// <summary>
  182.                 /// Multiply the vector v by the matrix in place.
  183.                 /// v points to an array of at least four values,
  184.                 /// representing R, G, B and A.
  185.                 /// </summary>
  186.                 public float[] TransformVector(float[] v)
  187.                 {
  188.                         return TransformVector(v, false);
  189.                 }
  190.  
  191.                 public static float[] Color2Vector(Color c)
  192.                 {
  193.                         if (c == null) return null;
  194.                         float[] p = new float[4];
  195.                         p[0] = (float)c.R;
  196.                         p[1] = (float)c.G;
  197.                         p[2] = (float)c.B;
  198.                         p[3] = (float)c.A;
  199.                         return p;
  200.                 }
  201.  
  202.                 public static Color Vector2Color(float[] p)
  203.                 {
  204.                         if (p == null || (p.Length < 4))
  205.                         {
  206.                                 throw new ArgumentException();
  207.                         }
  208.                         return Color.FromArgb((int)p[3], (int)p[0], (int)p[1], (int)p[2]);
  209.                 }
  210.  
  211.                 public float[] TransformVector(float[] v, bool normalize)
  212.                 {
  213.                         if (v == null || (v.Length < 4))
  214.                         {
  215.                                 throw new ArgumentException();
  216.                         }
  217.                         float[] temp = new float[4];
  218.                         for (int x = 0; x < 4; x++)
  219.                         {
  220.                                 temp[x] = 255.0f * m[4, x];
  221.                                 for (int y = 0; y < 4; y++)
  222.                                 {
  223.                                         temp[x] += v[y] * m[y, x];
  224.                                 }
  225.                         }
  226.                         for (int x = 0; x < 4; x++)
  227.                         {
  228.                                 v[x] = temp[x];
  229.                                 if (normalize)
  230.                                 {
  231.                                         if (v[x] < 0) v[x] = 0.0f;
  232.                                         else if (v[x] > 255.0f) v[x] = 255.0f;
  233.                                 }
  234.                         }
  235.                         return v;
  236.                 }
  237.  
  238.                 /// <summary>
  239.                 /// Multiply each color by the matrix.
  240.                 /// </summary>
  241.                 public Color[] TransformColors(Color[] colors)
  242.                 {
  243.                         if (colors == null) return null;
  244.                         for (int i = 0; i < colors.Length; i++)
  245.                         {
  246.                                 colors[i] = Vector2Color(
  247.                                         TransformVector(
  248.                                                 Color2Vector(colors[i]), true));
  249.                         }
  250.                         return colors;
  251.                 }
  252.  
  253.  
  254.                 public void Multiply(QColorMatrix matrix)
  255.                 {
  256.                         Multiply(matrix, MatrixOrder.MatrixOrderPrepend);
  257.                 }
  258.  
  259.                 /// <summary>
  260.                 /// Unlike the original C++ code we multiply all value here.
  261.                 /// </summary>
  262.                 public void Multiply(QColorMatrix matrix, MatrixOrder order)
  263.                 {
  264.                         if (matrix == null) throw new ArgumentException();
  265.                         float[,] a = null;
  266.                         float[,] b = null;
  267.  
  268.                         if (order == MatrixOrder.MatrixOrderAppend)
  269.                         {
  270.                                 a = matrix.m;
  271.                                 b = m;
  272.                         }
  273.                         else
  274.                         {
  275.                                 a = m;
  276.                                 b = matrix.m;
  277.                         }
  278.  
  279.                         float[,] temp = new float[MatrixLength, MatrixLength];
  280.                         for (int y = 0; y < MatrixLength; y++)
  281.                         {
  282.                                 for (int x = 0; x < MatrixLength; x++)
  283.                                 {
  284.                                         float t = 0;
  285.                                         for (int i = 0; i < MatrixLength; i++)
  286.                                         {
  287.                                                 t += b[y, i] * a[i, x];
  288.                                         }
  289.                                         temp[y, x] = t;
  290.                                 }
  291.                         }
  292.                         for (int y = 0; y < MatrixLength; y++)
  293.                         {
  294.                                 for (int x = 0; x < MatrixLength; x++)
  295.                                 {
  296.                                         m[y, x] = temp[y, x];
  297.                                 }
  298.                         }
  299.                 }
  300.  
  301.                 #endregion core
  302.  
  303.                 #region scale
  304.  
  305.                 /// <summary>
  306.                 /// Update this matrix with the product of itself and a scaling vector.
  307.                 /// </summary>
  308.                 public void Scale(float scaleRed, float scaleGreen, float scaleBlue,
  309.                         float scaleOpacity)
  310.                 {
  311.                         Scale(scaleRed, scaleGreen, scaleBlue,
  312.                                 scaleOpacity, MatrixOrder.MatrixOrderPrepend);
  313.                 }
  314.  
  315.                 public void Scale(float scaleRed, float scaleGreen, float scaleBlue,
  316.                         float scaleOpacity, MatrixOrder order)
  317.                 {
  318.                         QColorMatrix qm = new QColorMatrix();
  319.                         qm.m[0, 0] = scaleRed;
  320.                         qm.m[1, 1] = scaleGreen;
  321.                         qm.m[2, 2] = scaleBlue;
  322.                         qm.m[3, 3] = scaleOpacity;
  323.                         Multiply(qm, order);
  324.                 }
  325.  
  326.                 /// <summary>
  327.                 /// Scale just the three colors with the same amount, leave opacity unchanged
  328.                 /// </summary>
  329.                 public void ScaleColors(float scale)
  330.                 {
  331.                         ScaleColors(scale, MatrixOrder.MatrixOrderPrepend);
  332.                 }
  333.  
  334.                 public void ScaleColors(float scale, MatrixOrder order)
  335.                 {
  336.                         Scale(scale, scale, scale, 1.0f, order);
  337.                 }
  338.  
  339.                 public void ScaleOpacity(float scaleOpacity)
  340.                 {
  341.                         ScaleOpacity(scaleOpacity, MatrixOrder.MatrixOrderPrepend);
  342.                 }
  343.  
  344.                 public void ScaleOpacity(float scaleOpacity, MatrixOrder order)
  345.                 {
  346.                         Scale(1.0f, 1.0f, 1.0f, scaleOpacity, order);
  347.                 }
  348.  
  349.                 #endregion scale
  350.  
  351.                 #region traslate
  352.  
  353.                 /// <summary>
  354.                 /// Update this matrix with the product of itself and a translation vector.
  355.                 /// </summary>
  356.                 public void Translate(float offsetRed, float offsetGreen, float offsetBlue,
  357.                         float offsetOpacity)
  358.                 {
  359.                         Translate(offsetRed, offsetGreen, offsetBlue,
  360.                                 offsetOpacity, MatrixOrder.MatrixOrderPrepend);
  361.                 }
  362.  
  363.                 public void Translate(float offsetRed, float offsetGreen, float offsetBlue,
  364.                         float offsetOpacity, MatrixOrder order)
  365.                 {
  366.                         QColorMatrix qm = new QColorMatrix();
  367.                         qm.m[4, 0] = offsetRed;
  368.                         qm.m[4, 1] = offsetGreen;
  369.                         qm.m[4, 2] = offsetBlue;
  370.                         qm.m[4, 3] = offsetOpacity;
  371.                         Multiply(qm, order);
  372.                 }
  373.  
  374.                 public void TranslateColors(float offset)
  375.                 {
  376.                         TranslateColors(offset, MatrixOrder.MatrixOrderPrepend);
  377.                 }
  378.  
  379.                 public void TranslateColors(float offset, MatrixOrder order)
  380.                 {
  381.                         Translate(offset, offset, offset, 0.0f, order);
  382.                 }
  383.  
  384.                 public void TranslateOpacity(float offsetOpacity)
  385.                 {
  386.                         TranslateOpacity(offsetOpacity, MatrixOrder.MatrixOrderPrepend);
  387.                 }
  388.  
  389.                 public void TranslateOpacity(float offsetOpacity, MatrixOrder order)
  390.                 {
  391.                         Translate(0.0f, 0.0f, 0.0f, offsetOpacity, order);
  392.                 }
  393.  
  394.                 #endregion traslate
  395.  
  396.                 #region rotate
  397.  
  398.                 // Rotate the matrix around one of the color axes. The color of the rotation
  399.                 // axis is unchanged, the other two colors are rotated in color space.
  400.                 // The angle phi is in degrees (-180.0f... 180.0f).
  401.  
  402.                 public void RotateRed(float phi)
  403.                 {
  404.                         RotateRed(phi, MatrixOrder.MatrixOrderPrepend);
  405.                 }
  406.                 public void RotateGreen(float phi)
  407.                 {
  408.                         RotateGreen(phi, MatrixOrder.MatrixOrderPrepend);
  409.                 }
  410.                 public void RotateBlue(float phi)
  411.                 {
  412.                         RotateBlue(phi, MatrixOrder.MatrixOrderPrepend);
  413.                 }
  414.                 public void RotateRed(float phi, MatrixOrder order)
  415.                 {
  416.                         RotateColor(phi, 2, 1, order);
  417.                 }
  418.                 public void RotateGreen(float phi, MatrixOrder order)
  419.                 {
  420.                         RotateColor(phi, 0, 2, order);
  421.                 }
  422.                 public void RotateBlue(float phi, MatrixOrder order)
  423.                 {
  424.                         RotateColor(phi, 1, 0, order);
  425.                 }
  426.  
  427.                 #endregion rotate
  428.  
  429.                 #region shear
  430.  
  431.                 // Shear the matrix in one of the color planes. The color of the color plane
  432.                 // is influenced by the two other colors.
  433.  
  434.                 public void ShearRed(float green, float blue)
  435.                 {
  436.                         ShearRed(green, blue, MatrixOrder.MatrixOrderPrepend);
  437.                 }
  438.  
  439.                 public void ShearGreen(float red, float blue)
  440.                 {
  441.                         ShearGreen(red, blue, MatrixOrder.MatrixOrderPrepend);
  442.                 }
  443.  
  444.                 public void ShearBlue(float red, float green)
  445.                 {
  446.                         ShearBlue(red, green, MatrixOrder.MatrixOrderPrepend);
  447.                 }
  448.  
  449.                 public void ShearRed(float green, float blue, MatrixOrder order)
  450.                 {
  451.                         ShearColor(0, 1, green, 2, blue, order);
  452.                 }
  453.  
  454.                 public void ShearGreen(float red, float blue, MatrixOrder order)
  455.                 {
  456.                         ShearColor(1, 0, red, 2, blue, order);
  457.                 }
  458.  
  459.                 public void ShearBlue(float red, float green, MatrixOrder order)
  460.                 {
  461.                         ShearColor(2, 0, red, 1, green, order);
  462.                 }
  463.  
  464.                 #endregion shear
  465.  
  466.                 #region HueSat
  467.  
  468.                 public void SetSaturation(float saturation)
  469.                 {
  470.                         SetSaturation(saturation, MatrixOrder.MatrixOrderPrepend);
  471.                 }
  472.  
  473.                 /// <summary>
  474.                 /// Set the saturation of the matrix. Saturation of 0.0f yields B&W, 1.0f is neutral.
  475.                 /// </summary>
  476.                 public void SetSaturation(float saturation, MatrixOrder order)
  477.                 {
  478.                         // For the theory behind this, see the web sites at the top of this file.
  479.                         // In short: if saturation is 1.0f, m becomes the identity matrix, and this matrix is
  480.                         // unchanged. If saturation is 0.0f, each color is scaled by it's luminance weight.
  481.                         float satCompl = 1.0f - saturation;
  482.                         float satComplR = lumR * satCompl;
  483.                         float satComplG = lumG * satCompl;
  484.                         float satComplB = lumB * satCompl;
  485.  
  486.                         float[,] tm = new float[,]
  487.             {
  488.                 {satComplR + saturation,        satComplR,      satComplR,      0.0f, 0.0f} ,
  489.                 {satComplG,     satComplG + saturation, satComplG,      0.0f, 0.0f},
  490.                         {satComplB,     satComplB,      satComplB + saturation, 0.0f, 0.0f},
  491.                         {0.0f,  0.0f,   0.0f,   1.0f,   0.0f},
  492.                         {0.0f,  0.0f,   0.0f,   0.0f,   1.0f}
  493.             };
  494.  
  495.                         QColorMatrix qm = new QColorMatrix(tm);
  496.                         Multiply(qm, order);
  497.                 }
  498.  
  499.                 /// <summary>
  500.                 /// Rotate the hue around the grey axis, keeping luminance fixed. Greys are fixed,
  501.                 /// all other colors change.
  502.                 /// </summary>
  503.                 public void RotateHue(float phi)
  504.                 {
  505.                         InitHue();
  506.                         // Rotate the grey vector to the blue axis.
  507.                         Multiply(preHue, MatrixOrder.MatrixOrderAppend);
  508.                         // Rotate around the blue axis
  509.                         RotateBlue(phi, MatrixOrder.MatrixOrderAppend);
  510.                         Multiply(postHue, MatrixOrder.MatrixOrderAppend);
  511.                 }
  512.  
  513.                 #endregion HueSat
  514.  
  515.                 #region convenience
  516.  
  517.                 public void SetContrast(float scale)
  518.                 {
  519.                         ScaleColors(scale);
  520.                 }
  521.  
  522.                 public void SetBrightness(float offset)
  523.                 {
  524.                         TranslateColors(offset, MatrixOrder.MatrixOrderAppend);
  525.                 }
  526.  
  527.                 public void SetSaturation2(float saturation)
  528.                 {
  529.                         SetSaturation(saturation, MatrixOrder.MatrixOrderAppend);
  530.                 }
  531.  
  532.                 #endregion convenience
  533.  
  534.                 #region private
  535.  
  536.                 private static void InitHue()
  537.                 {
  538.                         const float greenRotation = 35.0f;
  539.                         //      const REAL greenRotation = 39.182655f;
  540.  
  541.                         // NOTE: theoretically, greenRotation should have the value of 39.182655 degrees,
  542.                         // being the angle for which the sine is 1/(sqrt(3)), and the cosine is sqrt(2/3).
  543.                         // However, I found that using a slightly smaller angle works better.
  544.                         // In particular, the greys in the image are not visibly affected with the smaller
  545.                         // angle, while they deviate a little bit with the theoretical value.
  546.                         // An explanation escapes me for now.
  547.                         // If you rather stick with the theory, change the comments in the previous lines.
  548.  
  549.  
  550.                         if (!initialized)
  551.                         {
  552.                                 initialized = true;
  553.                                 // Rotating the hue of an image is a rather convoluted task, involving several matrix
  554.                                 // multiplications. For efficiency, we prepare two static matrices.
  555.                                 // This is by far the most complicated part of this class. For the background
  556.                                 // theory, refer to the sgi-sites mentioned at the top of this file.
  557.  
  558.                                 // Prepare the preHue matrix.
  559.                                 // Rotate the grey vector in the green plane.
  560.                                 preHue.RotateRed(45.0f);
  561.  
  562.                                 // Next, rotate it again in the green plane, so it coincides with the blue axis.
  563.                                 preHue.RotateGreen(-greenRotation, MatrixOrder.MatrixOrderAppend);
  564.  
  565.                                 // Hue rotations keep the color luminations constant, so that only the hues change
  566.                                 // visible. To accomplish that, we shear the blue plane.
  567.                                 float[] lum = new float[] { lumR, lumG, lumB, 1.0f };
  568.  
  569.                                 // Transform the luminance vector.
  570.                                 preHue.TransformVector(lum);
  571.  
  572.                                 // Calculate the shear factors for red and green.
  573.                                 float red = lum[0] / lum[2];
  574.                                 float green = lum[1] / lum[2];
  575.  
  576.                                 // Shear the blue plane.
  577.                                 preHue.ShearBlue(red, green, MatrixOrder.MatrixOrderAppend);
  578.  
  579.                                 // Prepare the postHue matrix. This holds the opposite transformations of the
  580.                                 // preHue matrix. In fact, postHue is the inversion of preHue.
  581.                                 postHue.ShearBlue(-red, -green);
  582.                                 postHue.RotateGreen(greenRotation, MatrixOrder.MatrixOrderAppend);
  583.                                 postHue.RotateRed(-45.0f, MatrixOrder.MatrixOrderAppend);
  584.                         }
  585.                 }
  586.  
  587.                 /// <summary>
  588.                 /// x and y are the indices of the value to receive the sin(phi) value
  589.                 /// </summary>
  590.                 /// <param name="phi">phi is in degrees</param>
  591.                 private void RotateColor(float phi, int x, int y, MatrixOrder order)
  592.                 {
  593.                         phi *= rad;
  594.                         QColorMatrix qm = new QColorMatrix();
  595.  
  596.                         qm.m[x, x] = qm.m[y, y] = (float)Math.Cos(phi);
  597.  
  598.                         float s = (float)Math.Sin(phi);
  599.                         qm.m[y, x] = s;
  600.                         qm.m[x, y] = -s;
  601.  
  602.                         Multiply(qm, order);
  603.                 }
  604.  
  605.                 private void ShearColor(int x, int y1, float d1, int y2, float d2, MatrixOrder order)
  606.                 {
  607.                         QColorMatrix qm = new QColorMatrix();
  608.                         qm.m[y1, x] = d1;
  609.                         qm.m[y2, x] = d2;
  610.                         Multiply(qm, order);
  611.                 }
  612.  
  613.                 private void Copy(QColorMatrix qm)
  614.                 {
  615.                         if (qm == null)
  616.                         {
  617.                                 Reset();
  618.                                 return;
  619.                         }
  620.                         Copy(qm.m);
  621.                 }
  622.  
  623.                 private void Copy(float[,] m)
  624.                 {
  625.                         if ((m == null) || (m.Length != this.m.Length))
  626.                         {
  627.                                 throw new ArgumentException();
  628.                         }
  629.                         Array.Copy(m, this.m, m.Length);
  630.                 }
  631.  
  632.                 #endregion private
  633.  
  634.         }//EOC
  635. }
RAW Paste Data
We use cookies for various purposes including analytics. By continuing to use Pastebin, you agree to our use of cookies as described in the Cookies Policy. OK, I Understand
Not a member of Pastebin yet?
Sign Up, it unlocks many cool features!
 
Top