Пример #1
0
    //--------------------------------------------------------------
    #region Convolution
    //--------------------------------------------------------------

    /// <summary>
    /// Applies the a filter kernel to the specified image.
    /// </summary>
    /// <param name="image">The image.</param>
    /// <param name="kernel">The filter kernel. (Needs to be square.)</param>
    /// <param name="wrapMode">The texture address mode.</param>
    /// <exception cref="ArgumentNullException">
    /// <paramref name="image"/> or <paramref name="kernel"/> is <see langword="null"/>.
    /// </exception>
    /// <exception cref="ArgumentException">
    /// <paramref name="kernel"/> is non-square.
    /// </exception>
    public static void Convolve(Image image, float[,] kernel, TextureAddressMode wrapMode)
    {
      if (image == null)
        throw new ArgumentNullException("image");
      if (kernel == null)
        throw new ArgumentNullException("kernel");
      if (kernel.GetLength(0) != kernel.GetLength(1))
        throw new ArgumentException("Filter kernel needs to be square.", "kernel");

      int width = image.Width;
      int height = image.Height;
      int kernelSize = kernel.GetLength(0);
      int kernelOffset = kernelSize / 2;

      var tmpImage = new Image(width, height, image.Format);
      Buffer.BlockCopy(image.Data, 0, tmpImage.Data, 0, image.Data.Length);

      // ReSharper disable AccessToDisposedClosure
      using (var tempImage4F = new ImageAccessor(tmpImage))
      using (var image4F = new ImageAccessor(image))
      {
#if SINGLE_THREADED
        for (int y = 0; y < height; y++)
#else
        Parallel.For(0, height, y =>
#endif
        {
          for (int x = 0; x < width; x++)
          {
            // Apply 2D kernel at (x, y).
            Vector4F color = new Vector4F();
            for (int row = 0; row < kernelSize; row++)
            {
              int srcY = y + row - kernelOffset;
              for (int column = 0; column < kernelSize; column++)
              {
                int srcX = x + column - kernelOffset;
                color += kernel[row, column] * tempImage4F.GetPixel(srcX, srcY, wrapMode);
              }
            }

            image4F.SetPixel(x, y, color);
          }
        }
#if !SINGLE_THREADED
        );
#endif
      }
      // ReSharper restore AccessToDisposedClosure
    }
Пример #2
0
    private static void Resize2D(Image srcImage, Image dstImage, bool alphaTransparency, TextureAddressMode wrapMode, PolyphaseKernel kernelX, PolyphaseKernel kernelY)
    {
      var tmpImage = new Image(dstImage.Width, srcImage.Height, srcImage.Format);

      // ReSharper disable AccessToDisposedClosure
      using (var srcImage4F = new ImageAccessor(srcImage))
      using (var tmpImage4F = new ImageAccessor(tmpImage))
      using (var dstImage4F = new ImageAccessor(dstImage))
      {
        // Resize horizontally: srcImage --> tmpImage
        {
          float scale = (float)tmpImage4F.Width / srcImage4F.Width;
          float inverseScale = 1.0f / scale;

#if SINGLE_THREADED
          for (int y = 0; y < tmpImage4F.Height; y++)
#else
          Parallel.For(0, tmpImage4F.Height, y =>
#endif
          {
            // Apply polyphase kernel horizontally.
            for (int x = 0; x < tmpImage4F.Width; x++)
            {
              float center = (x + 0.5f) * inverseScale;

              int left = (int)Math.Floor(center - kernelX.Width);
              int right = (int)Math.Ceiling(center + kernelX.Width);
              Debug.Assert(right - left <= kernelX.WindowSize);

              float totalRgbWeights = 0.0f;
              Vector4F sum = new Vector4F();
              for (int i = 0; i < kernelX.WindowSize; i++)
              {
                Vector4F color = srcImage4F.GetPixel(left + i, y, wrapMode);

                //if (Numeric.IsNaN(color.X) || Numeric.IsNaN(color.Y) || Numeric.IsNaN(color.Z) || Numeric.IsNaN(color.W)
                //   || color.X < 0 || color.Y < 0 || color.Z < 0 || color.W < 0
                //   || color.X > 1 || color.Y > 1 || color.Z > 1 || color.W > 1)
                //  Debugger.Break();

                const float alphaEpsilon = 1.0f / 256.0f;
                float alpha = alphaTransparency ? color.W + alphaEpsilon : 1.0f;

                float weight = kernelX.Weights[x, i];
                float rgbWeight = weight * alpha;
                totalRgbWeights += rgbWeight;

                sum.X += color.X * rgbWeight;
                sum.Y += color.Y * rgbWeight;
                sum.Z += color.Z * rgbWeight;
                sum.W += color.W * weight;

                //if (Numeric.IsNaN(sum.X) || Numeric.IsNaN(sum.Y) || Numeric.IsNaN(sum.Z) || Numeric.IsNaN(sum.W)
                //   || sum.X < 0 || sum.Y < 0 || sum.Z < 0 || sum.W < 0
                //   || sum.X > 1 || sum.Y > 1 || sum.Z > 1 || sum.W > 1)
                //  Debugger.Break();
              }

              float f = 1 / totalRgbWeights;
              sum.X *= f;
              sum.Y *= f;
              sum.Z *= f;

              //if (Numeric.IsNaN(sum.X) || Numeric.IsNaN(sum.Y) || Numeric.IsNaN(sum.Z) || Numeric.IsNaN(sum.W)
              //   || sum.X < 0 || sum.Y < 0 || sum.Z < 0 || sum.W < 0
              //   || sum.X > 1 || sum.Y > 1 || sum.Z > 1 || sum.W > 1)
              //  Debugger.Break();

              tmpImage4F.SetPixel(x, y, sum);
            }
          }
#if !SINGLE_THREADED
          );
#endif
        }

        // Resize vertically: tmpImage --> dstImage
        {
          float scale = (float)dstImage4F.Height / tmpImage4F.Height;
          float inverseScale = 1.0f / scale;

#if SINGLE_THREADED
          for (int x = 0; x < dstImage4F.Width; x++)
#else
          Parallel.For(0, dstImage4F.Width, x =>
#endif
          {
            // Apply polyphase kernel vertically.
            for (int y = 0; y < dstImage4F.Height; y++)
            {
              float center = (y + 0.5f) * inverseScale;

              int left = (int)Math.Floor(center - kernelY.Width);
              int right = (int)Math.Ceiling(center + kernelY.Width);
              Debug.Assert(right - left <= kernelY.WindowSize);

              float totalRgbWeights = 0.0f;
              Vector4F sum = new Vector4F();
              for (int i = 0; i < kernelY.WindowSize; i++)
              {
                Vector4F color = tmpImage4F.GetPixel(x, left + i, wrapMode);

                const float alphaEpsilon = 1.0f / 256.0f;
                float alpha = alphaTransparency ? color.W + alphaEpsilon : 1.0f;

                float weight = kernelY.Weights[y, i];
                float rgbWeight = weight * alpha;
                totalRgbWeights += rgbWeight;

                sum.X += color.X * rgbWeight;
                sum.Y += color.Y * rgbWeight;
                sum.Z += color.Z * rgbWeight;
                sum.W += color.W * weight;
              }

              float f = 1 / totalRgbWeights;
              sum.X *= f;
              sum.Y *= f;
              sum.Z *= f;

              dstImage4F.SetPixel(x, y, sum);
            }
          }
#if !SINGLE_THREADED
          );
#endif
        }
      }
      // ReSharper restore AccessToDisposedClosure
    }
Пример #3
0
        private static void Resize2D(Image srcImage, Image dstImage, bool alphaTransparency, TextureAddressMode wrapMode, PolyphaseKernel kernelX, PolyphaseKernel kernelY)
        {
            var tmpImage = new Image(dstImage.Width, srcImage.Height, srcImage.Format);

              // ReSharper disable AccessToDisposedClosure
              using (var srcImage4F = new ImageAccessor(srcImage))
              using (var tmpImage4F = new ImageAccessor(tmpImage))
              using (var dstImage4F = new ImageAccessor(dstImage))
              {
            // Resize horizontally: srcImage --> tmpImage
            {
              float scale = (float)tmpImage4F.Width / srcImage4F.Width;
              float inverseScale = 1.0f / scale;

            #if SINGLE_THREADED
              for (int y = 0; y < tmpImage4F.Height; y++)
            #else
              Parallel.For(0, tmpImage4F.Height, y =>
            #endif
              {
            // Apply polyphase kernel horizontally.
            for (int x = 0; x < tmpImage4F.Width; x++)
            {
              float center = (x + 0.5f) * inverseScale;

              int left = (int)Math.Floor(center - kernelX.Width);
              int right = (int)Math.Ceiling(center + kernelX.Width);
              Debug.Assert(right - left <= kernelX.WindowSize);

              float totalRgbWeights = 0.0f;
              Vector4F sum = new Vector4F();
              for (int i = 0; i < kernelX.WindowSize; i++)
              {
                Vector4F color = srcImage4F.GetPixel(left + i, y, wrapMode);

                //if (Numeric.IsNaN(color.X) || Numeric.IsNaN(color.Y) || Numeric.IsNaN(color.Z) || Numeric.IsNaN(color.W)
                //   || color.X < 0 || color.Y < 0 || color.Z < 0 || color.W < 0
                //   || color.X > 1 || color.Y > 1 || color.Z > 1 || color.W > 1)
                //  Debugger.Break();

                const float alphaEpsilon = 1.0f / 256.0f;
                float alpha = alphaTransparency ? color.W + alphaEpsilon : 1.0f;

                float weight = kernelX.Weights[x, i];
                float rgbWeight = weight * alpha;
                totalRgbWeights += rgbWeight;

                sum.X += color.X * rgbWeight;
                sum.Y += color.Y * rgbWeight;
                sum.Z += color.Z * rgbWeight;
                sum.W += color.W * weight;

                //if (Numeric.IsNaN(sum.X) || Numeric.IsNaN(sum.Y) || Numeric.IsNaN(sum.Z) || Numeric.IsNaN(sum.W)
                //   || sum.X < 0 || sum.Y < 0 || sum.Z < 0 || sum.W < 0
                //   || sum.X > 1 || sum.Y > 1 || sum.Z > 1 || sum.W > 1)
                //  Debugger.Break();
              }

              float f = 1 / totalRgbWeights;
              sum.X *= f;
              sum.Y *= f;
              sum.Z *= f;

              //if (Numeric.IsNaN(sum.X) || Numeric.IsNaN(sum.Y) || Numeric.IsNaN(sum.Z) || Numeric.IsNaN(sum.W)
              //   || sum.X < 0 || sum.Y < 0 || sum.Z < 0 || sum.W < 0
              //   || sum.X > 1 || sum.Y > 1 || sum.Z > 1 || sum.W > 1)
              //  Debugger.Break();

              tmpImage4F.SetPixel(x, y, sum);
            }
              }
            #if !SINGLE_THREADED
              );
            #endif
            }

            // Resize vertically: tmpImage --> dstImage
            {
              float scale = (float)dstImage4F.Height / tmpImage4F.Height;
              float inverseScale = 1.0f / scale;

            #if SINGLE_THREADED
              for (int x = 0; x < dstImage4F.Width; x++)
            #else
              Parallel.For(0, dstImage4F.Width, x =>
            #endif
              {
            // Apply polyphase kernel vertically.
            for (int y = 0; y < dstImage4F.Height; y++)
            {
              float center = (y + 0.5f) * inverseScale;

              int left = (int)Math.Floor(center - kernelY.Width);
              int right = (int)Math.Ceiling(center + kernelY.Width);
              Debug.Assert(right - left <= kernelY.WindowSize);

              float totalRgbWeights = 0.0f;
              Vector4F sum = new Vector4F();
              for (int i = 0; i < kernelY.WindowSize; i++)
              {
                Vector4F color = tmpImage4F.GetPixel(x, left + i, wrapMode);

                const float alphaEpsilon = 1.0f / 256.0f;
                float alpha = alphaTransparency ? color.W + alphaEpsilon : 1.0f;

                float weight = kernelY.Weights[y, i];
                float rgbWeight = weight * alpha;
                totalRgbWeights += rgbWeight;

                sum.X += color.X * rgbWeight;
                sum.Y += color.Y * rgbWeight;
                sum.Z += color.Z * rgbWeight;
                sum.W += color.W * weight;
              }

              float f = 1 / totalRgbWeights;
              sum.X *= f;
              sum.Y *= f;
              sum.Z *= f;

              dstImage4F.SetPixel(x, y, sum);
            }
              }
            #if !SINGLE_THREADED
              );
            #endif
            }
              }
              // ReSharper restore AccessToDisposedClosure
        }
Пример #4
0
        //--------------------------------------------------------------
        /// <summary>
        /// Applies the a filter kernel to the specified image.
        /// </summary>
        /// <param name="image">The image.</param>
        /// <param name="kernel">The filter kernel. (Needs to be square.)</param>
        /// <param name="wrapMode">The texture address mode.</param>
        /// <exception cref="ArgumentNullException">
        /// <paramref name="image"/> or <paramref name="kernel"/> is <see langword="null"/>.
        /// </exception>
        /// <exception cref="ArgumentException">
        /// <paramref name="kernel"/> is non-square.
        /// </exception>
        public static void Convolve(Image image, float[,] kernel, TextureAddressMode wrapMode)
        {
            if (image == null)
            throw new ArgumentNullException("image");
              if (kernel == null)
            throw new ArgumentNullException("kernel");
              if (kernel.GetLength(0) != kernel.GetLength(1))
            throw new ArgumentException("Filter kernel needs to be square.", "kernel");

              int width = image.Width;
              int height = image.Height;
              int kernelSize = kernel.GetLength(0);
              int kernelOffset = kernelSize / 2;

              var tmpImage = new Image(width, height, image.Format);
              Buffer.BlockCopy(image.Data, 0, tmpImage.Data, 0, image.Data.Length);

              // ReSharper disable AccessToDisposedClosure
              using (var tempImage4F = new ImageAccessor(tmpImage))
              using (var image4F = new ImageAccessor(image))
              {
            #if SINGLE_THREADED
            for (int y = 0; y < height; y++)
            #else
            Parallel.For(0, height, y =>
            #endif
            {
              for (int x = 0; x < width; x++)
              {
            // Apply 2D kernel at (x, y).
            Vector4F color = new Vector4F();
            for (int row = 0; row < kernelSize; row++)
            {
              int srcY = y + row - kernelOffset;
              for (int column = 0; column < kernelSize; column++)
              {
                int srcX = x + column - kernelOffset;
                color += kernel[row, column] * tempImage4F.GetPixel(srcX, srcY, wrapMode);
              }
            }

            image4F.SetPixel(x, y, color);
              }
            }
            #if !SINGLE_THREADED
            );
            #endif
              }
              // ReSharper restore AccessToDisposedClosure
        }
Пример #5
0
        /// <summary>
        /// Determines the alpha test coverage of the specified image.
        /// </summary>
        /// <param name="image">The image (<see cref="DataFormat.R32G32B32A32_FLOAT"/>).</param>
        /// <param name="referenceAlpha">The reference value used in the alpha test.</param>
        /// <param name="alphaScale">A scale factor applied to the alpha value.</param>
        /// <returns>The alpha test coverage of the highest mipmap level.</returns>
        /// <remarks>
        /// The alpha test coverage is the relative amount of pixel that pass the alpha test. This
        /// method assumes that pixels with an alpha value greater or equal to
        /// <paramref name="referenceAlpha" /> pass the alpha test.
        /// </remarks>
        /// <exception cref="ArgumentNullException">
        /// <paramref name="image"/> is <see langword="null"/>.
        /// </exception>
        private static float GetAlphaTestCoverage(Image image, float referenceAlpha, float alphaScale = 1)
        {
            if (image == null)
            throw new ArgumentNullException("image");

              float coverage = 0;
              int size = image.Width * image.Height;
              using (var image4F = new ImageAccessor(image))
              {
            for (int i = 0; i < size; i++)
            {
              Vector4F color = image4F.GetPixel(i);
              float alpha = MathHelper.Clamp(color.W * alphaScale, 0, 1);
              if (alpha >= referenceAlpha)
            coverage++;
            }
              }

              return coverage / size;
        }
Пример #6
0
        /// <summary>
        /// Rotates the image counter-clockwise 0°, 90°, 180°, or 270°.
        /// </summary>
        /// <param name="srcImage">The unrotated image.</param>
        /// <param name="dstImage">The rotated image.</param>
        /// <param name="degrees">
        /// The angle in degrees. Allowed values: -270, -180, -90, 0, 90, 180, 270
        /// </param>
        private static void Rotate(Image srcImage, Image dstImage, int degrees)
        {
            Debug.Assert(srcImage != null);
              Debug.Assert(dstImage != null);
              Debug.Assert(srcImage.Format == dstImage.Format);

              using (var src = new ImageAccessor(srcImage))
              using (var dst = new ImageAccessor(dstImage))
              {
            int width = srcImage.Width;
            int height = srcImage.Height;
            switch (degrees)
            {
              case -360:
              case 0:
              case 360:
            Debug.Assert(srcImage.Data.Length == dstImage.Data.Length);
            Buffer.BlockCopy(srcImage.Data, 0, dstImage.Data, 0, srcImage.Data.Length);
            break;

              case -180:
              case 180:
            Debug.Assert(srcImage.Width == dstImage.Width || srcImage.Height == dstImage.Height);
            for (int y = 0; y < height; y++)
              for (int x = 0; x < width; x++)
                dst.SetPixel(width - x - 1, height - y - 1, src.GetPixel(x, y));
            break;

              case -270:
              case 90:
            Debug.Assert(srcImage.Width == dstImage.Height || srcImage.Height == dstImage.Width);
            for (int y = 0; y < height; y++)
              for (int x = 0; x < width; x++)
                dst.SetPixel(y, width - x - 1, src.GetPixel(x, y));
            break;

              case -90:
              case 270:
            Debug.Assert(srcImage.Width == dstImage.Height || srcImage.Height == dstImage.Width);
            for (int y = 0; y < height; y++)
              for (int x = 0; x < width; x++)
                dst.SetPixel(height - y - 1, x, src.GetPixel(x, y));
            break;

              default:
            Debug.Fail("Invalid rotation angle.");
            break;
            }
              }
        }
Пример #7
0
        /// <summary>
        /// Expands the image from unsigned normalized values [0, 1] to signed normalized values
        /// [-1, 1]. (Assumes input data is normal map!)
        /// </summary>
        /// <param name="image">The image (<see cref="DataFormat.R32G32B32A32_FLOAT"/>).</param>
        /// <exception cref="ArgumentNullException">
        /// <paramref name="image"/> is <see langword="null"/>.
        /// </exception>
        public static void UnpackNormals(Image image)
        {
            if (image == null)
            throw new ArgumentNullException("image");

              // ReSharper disable AccessToDisposedClosure
              using (var image4F = new ImageAccessor(image))
              {
            #if SINGLE_THREADED
            for (int y = 0; y < image4F.Height; y++)
            #else
            Parallel.For(0, image4F.Height, y =>
            #endif
            {
              for (int x = 0; x < image4F.Width; x++)
              {
            // Only for normal map: (byte)128 maps to (float)0.
            Vector4F color = image4F.GetPixel(x, y);
            color.X = color.X * 255 / 128 - 1;
            color.Y = color.Y * 255 / 128 - 1;
            color.Z = color.Z * 255 / 128 - 1;
            image4F.SetPixel(x, y, color);
              }
            }
            #if !SINGLE_THREADED
            );
            #endif
              }
              // ReSharper restore AccessToDisposedClosure
        }
Пример #8
0
        /// <summary>
        /// Applies color keying to the specified image (<see cref="DataFormat.R32G32B32A32_FLOAT"/>).
        /// </summary>
        /// <param name="image">The image (<see cref="DataFormat.R32G32B32A32_FLOAT"/>).</param>
        /// <param name="colorKey">Color of the color key.</param>
        /// <exception cref="ArgumentNullException">
        /// <paramref name="image"/> is <see langword="null"/>.
        /// </exception>
        public static void ApplyColorKey(Image image, Vector4F colorKey)
        {
            if (image == null)
            throw new ArgumentNullException("image");

              // ReSharper disable AccessToDisposedClosure
              using (var image4F = new ImageAccessor(image))
              {
            #if SINGLE_THREADED
            for (int y = 0; y < image4F.Height; y++)
            #else
            Parallel.For(0, image4F.Height, y =>
            #endif
            {
              for (int x = 0; x < image4F.Width; x++)
              {
            Vector4F color = image4F.GetPixel(x, y);
            if (Vector4F.AreNumericallyEqual(color, colorKey))
              image4F.SetPixel(x, y, new Vector4F(color.X, color.Y, color.Z, 0));
              }
            }
            #if !SINGLE_THREADED
            );
            #endif
              }
        }
Пример #9
0
        /// <summary>
        /// Prepares a normal map for compression using DXT5 (a.k.a. DXT5nm).
        /// </summary>
        /// <param name="image">The image (<see cref="DataFormat.R32G32B32A32_FLOAT"/>).</param>
        /// <param name="invertY"><see langword="true"/> to invert the y component.</param>
        /// <exception cref="ArgumentNullException">
        /// <paramref name="image"/> is <see langword="null"/>.
        /// </exception>
        public static void ProcessNormals(Image image, bool invertY)
        {
            if (image == null)
            throw new ArgumentNullException("image");

              float sign = invertY ? -1 : 1;

              // ReSharper disable AccessToDisposedClosure
              using (var image4F = new ImageAccessor(image))
              {
            #if SINGLE_THREADED
            for (int y = 0; y < image4F.Height; y++)
            #else
            Parallel.For(0, image4F.Height, y =>
            #endif
            {
              for (int x = 0; x < image4F.Width; x++)
              {
            Vector4F v = image4F.GetPixel(x, y);
            Vector3F normal = new Vector3F(v.X, v.Y, v.Z);

            // Renormalize normals. (Important for higher mipmap levels.)
            if (!normal.TryNormalize())
              normal = new Vector3F(0, 0, 1);

            // Convert to DXT5nm (xGxR).
            v.X = 0.0f;
            v.Y = sign * normal.Y * 0.5f + 0.5f;
            v.Z = 0.0f;
            v.W = normal.X * 0.5f + 0.5f;

            image4F.SetPixel(x, y, v);
              }
            }
            #if !SINGLE_THREADED
            );
            #endif
              }
              // ReSharper restore AccessToDisposedClosure
        }
Пример #10
0
        /// <summary>
        /// Scales the alpha values of the image to create the desired alpha test coverage.
        /// </summary>
        /// <param name="image">The image (<see cref="DataFormat.R32G32B32A32_FLOAT"/>).</param>
        /// <param name="referenceAlpha">The reference alpha.</param>
        /// <param name="desiredCoverage">The desired alpha test coverage.</param>
        /// <param name="premultipliedAlpha">
        /// <see langword="true"/> if texture uses premultiplied alpha.
        /// </param>
        /// <exception cref="ArgumentNullException">
        /// <paramref name="image"/> is <see langword="null"/>.
        /// </exception>
        public static void ScaleAlphaToCoverage(Image image, float referenceAlpha, float desiredCoverage, bool premultipliedAlpha)
        {
            if (image == null)
            throw new ArgumentNullException("image");

              // To reach the desired alpha test coverage, all alpha values need to be scaled
              // by a certain factor. One solution to find the scale factor is by doing a binary
              // search.
              float minAlphaScale = 0.0f;
              float maxAlphaScale = 8.0f;
              float alphaScale = 4.0f;

              // In the NVIDIA Texture Tools the binary search is limited to a hardcoded number
              // of steps.
              const int numberOfSteps = 10;
              for (int i = 0; i < numberOfSteps; i++)
              {
            float currentCoverage = GetAlphaTestCoverage(image, referenceAlpha, alphaScale);

            if (currentCoverage < desiredCoverage)
              minAlphaScale = alphaScale;
            else if (currentCoverage > desiredCoverage)
              maxAlphaScale = alphaScale;
            else
              break;

            alphaScale = (minAlphaScale + maxAlphaScale) / 2.0f;
              }

              if (premultipliedAlpha)
              {
            // ReSharper disable AccessToDisposedClosure
            using (var image4F = new ImageAccessor(image))
            {
            #if SINGLE_THREADED
              for (int y = 0; y < image4F.Height; y++)
            #else
              Parallel.For(0, image4F.Height, y =>
            #endif
              {
            for (int x = 0; x < image4F.Width; x++)
            {
              Vector4F color = image4F.GetPixel(x, y);
              float alpha = color.W;
              if (alpha > Numeric.EpsilonF)
              {
                // Undo premultiplication.
                float oneOverAlpha = 1 / alpha;
                color.X *= oneOverAlpha;
                color.Y *= oneOverAlpha;
                color.Z *= oneOverAlpha;

                // Scale alpha.
                alpha = MathHelper.Clamp(alpha * alphaScale, 0, 1);

                // Premultiply alpha.
                color.X *= alpha;
                color.Y *= alpha;
                color.Z *= alpha;
                color.W = alpha;
              }
              image4F.SetPixel(x, y, color);
            }
              }
            #if !SINGLE_THREADED
              );
            #endif
            }
            // ReSharper restore AccessToDisposedClosure
              }
              else
              {
            // ReSharper disable AccessToDisposedClosure
            using (var image4F = new ImageAccessor(image))
            {
            #if SINGLE_THREADED
              for (int y = 0; y < image4F.Height; y++)
            #else
              Parallel.For(0, image4F.Height, y =>
            #endif
              {
            for (int x = 0; x < image4F.Width; x++)
            {
              Vector4F color = image4F.GetPixel(x, y);
              color.W = MathHelper.Clamp(color.W * alphaScale, 0, 1);
              image4F.SetPixel(x, y, color);
            }
              }
            #if !SINGLE_THREADED
              );
            #endif
            }
            // ReSharper restore AccessToDisposedClosure
              }
        }
Пример #11
0
        /// <summary>
        /// Premultiplies the alpha value of the specified image.
        /// </summary>
        /// <param name="image">The image (<see cref="DataFormat.R32G32B32A32_FLOAT"/>).</param>
        /// <exception cref="ArgumentNullException">
        /// <paramref name="image"/> is <see langword="null"/>.
        /// </exception>
        public static void PremultiplyAlpha(Image image)
        {
            if (image == null)
            throw new ArgumentNullException("image");

              // ReSharper disable AccessToDisposedClosure
              using (var image4F = new ImageAccessor(image))
              {
            #if SINGLE_THREADED
            for (int y = 0; y < image4F.Height; y++)
            #else
            Parallel.For(0, image4F.Height, y =>
            #endif
            {
              for (int x = 0; x < image4F.Width; x++)
              {
            Vector4F color = image4F.GetPixel(x, y);
            if (color.W < 1.0f)
            {
              color.X *= color.W;
              color.Y *= color.W;
              color.Z *= color.W;
              image4F.SetPixel(x, y, color);
            }
              }
            }
            #if !SINGLE_THREADED
            );
            #endif
              }
              // ReSharper restore AccessToDisposedClosure
        }
Пример #12
0
        /// <summary>
        /// Converts the specified image from linear space to gamma space.
        /// </summary>
        /// <param name="image">The image (<see cref="DataFormat.R32G32B32A32_FLOAT"/>).</param>
        /// <param name="gamma">The gamma value.</param>
        /// <exception cref="ArgumentNullException">
        /// <paramref name="image"/> is <see langword="null"/>.
        /// </exception>
        public static void LinearToGamma(Image image, float gamma)
        {
            if (image == null)
            throw new ArgumentNullException("image");

              if (Numeric.AreEqual(gamma, 1.0f))
            return;

              if (gamma <= 0)
              {
            string message = string.Format(
              CultureInfo.InvariantCulture,
              "Invalid gamma value {0}. The gamma correction value must be greater than 0.",
              gamma);
            throw new ArgumentException(message, "gamma");
              }

              float gammaCorrection = 1.0f / gamma;

              // ReSharper disable AccessToDisposedClosure
              using (var image4F = new ImageAccessor(image))
              {
            #if SINGLE_THREADED
            for (int y = 0; y < image4F.Height; y++)
            #else
            Parallel.For(0, image4F.Height, y =>
            #endif
            {
              for (int x = 0; x < image4F.Width; x++)
              {
            Vector4F color = image4F.GetPixel(x, y);
            color.X = (float)Math.Pow(color.X, gammaCorrection);
            color.Y = (float)Math.Pow(color.Y, gammaCorrection);
            color.Z = (float)Math.Pow(color.Z, gammaCorrection);
            image4F.SetPixel(x, y, color);
              }
            }
            #if !SINGLE_THREADED
            );
            #endif
              }
              // ReSharper restore AccessToDisposedClosure
        }
Пример #13
0
        /// <summary>
        /// Determines whether the specified image uses the alpha channel.
        /// </summary>
        /// <param name="image">The image.</param>
        /// <param name="hasAlpha">
        /// <see langword="true"/> if <paramref name="image"/> requires an alpha channel; otherwise,
        /// <see langword="false"/> if <paramref name="image"/> is opaque.
        /// </param>
        /// <param name="hasFractionalAlpha">
        /// <see langword="true"/> if <paramref name="image"/> contains fractional alpha values;
        /// otherwise, <see langword="false"/> if <paramref name="image"/> is opaque or contains only
        /// binary alpha.
        /// </param>
        /// <exception cref="ArgumentNullException">
        /// <paramref name="image"/> is <see langword="null"/>.
        /// </exception>
        public static unsafe void HasAlpha(Image image, out bool hasAlpha, out bool hasFractionalAlpha)
        {
            if (image == null)
            throw new ArgumentNullException("image");

              hasAlpha = false;
              hasFractionalAlpha = false;
              int numberOfPixels = image.Width * image.Height;

              switch (image.Format)
              {
            case DataFormat.R32G32B32A32_FLOAT:
              using (var image4F = new ImageAccessor(image))
              {
            for (int i = 0; i < numberOfPixels; i++)
            {
              Vector4F color = image4F.GetPixel(i);
              if (Numeric.IsLess(color.W, 1.0f))
              {
                hasAlpha = true;
                if (Numeric.IsGreater(color.W, 0.0f))
                {
                  hasFractionalAlpha = true;
                  return;
                }
              }
            }
              }
              break;

            case DataFormat.R8G8B8A8_TYPELESS:
            case DataFormat.R8G8B8A8_UNORM:
            case DataFormat.R8G8B8A8_UNORM_SRGB:
            case DataFormat.R8G8B8A8_UINT:
            case DataFormat.R8G8B8A8_SNORM:
            case DataFormat.R8G8B8A8_SINT:
            case DataFormat.B8G8R8A8_UNORM:
            case DataFormat.B8G8R8A8_TYPELESS:
            case DataFormat.B8G8R8A8_UNORM_SRGB:
              {
            fixed (byte* dataPtr = image.Data)
            {
              byte* ptr = dataPtr + 3;
              for (int i = 0; i < numberOfPixels; i++)
              {
                byte a = *ptr;
                if (a < 0xFF)
                {
                  hasAlpha = true;
                  if (a > 0)
                  {
                    hasFractionalAlpha = true;
                    return;
                  }
                }

                ptr += 4;
              }
            }
              }
              break;

            default:
              throw new NotSupportedException(string.Format("The texture format ({0}) is not supported.", image.Format));
              }
        }
Пример #14
0
        /// <summary>
        /// Mirrors the image vertically.
        /// </summary>
        /// <param name="image">The image.</param>
        /// <exception cref="ArgumentNullException">
        /// <paramref name="image"/> is <see langword="null"/>.
        /// </exception>
        public static void FlipY(Image image)
        {
            if (image == null)
            throw new ArgumentNullException("image");

              using (var image4F = new ImageAccessor(image))
              {
            int width = image4F.Width;
            int height = image4F.Height;
            int halfHeight = height / 2;
            for (int y0 = 0; y0 < halfHeight; y0++)
            {
              for (int x = 0; x < width; x++)
              {
            int y1 = height - y0 - 1;
            Vector4F color0 = image4F.GetPixel(x, y0);
            Vector4F color1 = image4F.GetPixel(x, y1);
            image4F.SetPixel(x, y0, color1);
            image4F.SetPixel(x, y1, color0);
              }
            }
              }
        }
Пример #15
0
        /// <summary>
        /// Mirrors the image horizontally.
        /// </summary>
        /// <param name="image">The image.</param>
        /// <exception cref="ArgumentNullException">
        /// <paramref name="image"/> is <see langword="null"/>.
        /// </exception>
        public static void FlipX(Image image)
        {
            if (image == null)
            throw new ArgumentNullException("image");

              using (var image4F = new ImageAccessor(image))
              {
            int width = image4F.Width;
            int height = image4F.Height;
            int halfWidth = width / 2;
            for (int y = 0; y < height; y++)
            {
              for (int x0 = 0; x0 < halfWidth; x0++)
              {
            int x1 = width - x0 - 1;
            Vector4F color0 = image4F.GetPixel(x0, y);
            Vector4F color1 = image4F.GetPixel(x1, y);
            image4F.SetPixel(x0, y, color1);
            image4F.SetPixel(x1, y, color0);
              }
            }
              }
        }