【问题标题】:Performance optimization of multiple rotating images多个旋转图像的性能优化
【发布时间】:2014-05-21 13:07:38
【问题描述】:

我正在使用 C# windows 窗体进行一个项目,在该项目中我使用图片框来显示多个转速计。

每个图片框由一个背景图像和一个图像组成。背景图像是静态的,但图像(显示实际指示器)每秒会旋转和更新几次。

背景图片:http://i.stack.imgur.com/FBsKI.jpg

正面图片:http://i.stack.imgur.com/T0nJU.jpg

只要我有一个或两个转速计同时运行,它就可以很好地工作,但是当我添加更多时,它开始滞后很多。

这是我用于更新图片框的代码:

private void timer4_Tick(object sender, EventArgs e)
    {
        Bitmap image = RotateImage(indicatorImg, i++, false, true, Color.Transparent);
        pictureBox4.Image = image;
    }

我正在使用的 RotateImage 函数(感谢 RenniePet https://stackoverflow.com/a/16027561/3660713):

public static Bitmap RotateImage(Image inputImage, float angleDegrees, bool upsizeOk,
                                     bool clipOk, Color backgroundColor)
    {
        lock (lockObject)
        {
            // Test for zero rotation and return a clone of the input image
            if (angleDegrees == 0f)
                return (Bitmap)inputImage.Clone();

            // Set up old and new image dimensions, assuming upsizing not wanted and clipping OK
            int oldWidth = inputImage.Width;
            int oldHeight = inputImage.Height;
            int newWidth = oldWidth;
            int newHeight = oldHeight;
            float scaleFactor = 1f;

            // If upsizing wanted or clipping not OK calculate the size of the resulting bitmap
            if (upsizeOk || !clipOk)
            {
                double angleRadians = angleDegrees * Math.PI / 180d;

                double cos = Math.Abs(Math.Cos(angleRadians));
                double sin = Math.Abs(Math.Sin(angleRadians));
                newWidth = (int)Math.Round(oldWidth * cos + oldHeight * sin);
                newHeight = (int)Math.Round(oldWidth * sin + oldHeight * cos);
            }

            // If upsizing not wanted and clipping not OK need a scaling factor
            if (!upsizeOk && !clipOk)
            {
                scaleFactor = Math.Min((float)oldWidth / newWidth, (float)oldHeight / newHeight);
                newWidth = oldWidth;
                newHeight = oldHeight;
            }

            // Create the new bitmap object. If background color is transparent it must be 32-bit, 
            //  otherwise 24-bit is good enough.
            Bitmap newBitmap = new Bitmap(newWidth, newHeight, backgroundColor == Color.Transparent ?
                                             PixelFormat.Format32bppArgb : PixelFormat.Format24bppRgb);
            newBitmap.SetResolution(inputImage.HorizontalResolution, inputImage.VerticalResolution);

            // Create the Graphics object that does the work
            using (Graphics graphicsObject = Graphics.FromImage(newBitmap))
            {
                graphicsObject.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
                graphicsObject.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.HighQuality;
                graphicsObject.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;

                // Fill in the specified background color if necessary
                if (backgroundColor != Color.Transparent)
                    graphicsObject.Clear(backgroundColor);

                // Set up the built-in transformation matrix to do the rotation and maybe scaling
                graphicsObject.TranslateTransform(newWidth / 2f, newHeight / 2f);

                if (scaleFactor != 1f)
                    graphicsObject.ScaleTransform(scaleFactor, scaleFactor);

                graphicsObject.RotateTransform(angleDegrees);
                graphicsObject.TranslateTransform(-oldWidth / 2f, -oldHeight / 2f);

                // Draw the result 
                graphicsObject.DrawImage(inputImage, 0, 0);
            }

            return newBitmap;
        }
    }

关于如何提高性能的任何想法?有没有更好的方法来做到这一点?

【问题讨论】:

  • 你可以用不同的方式实现它(没有计时器),见here。除此之外,位图位图是最快的方法。您可以预渲染帧,然后在绘制图像时使用它们。它可以以缓存的方式完成:如果帧丢失 - 它被渲染然后重用,否则它保持 null 并且不使用内存。
  • 您可能需要使用当前相关的 .Net UI 技术检查My Answer 来回答一个非常相似的问题。
  • 仅供参考,我已经修改了我之前评论中链接的示例,以包括通过计时器随机移动的 20 个仪表,并且 UI 响应是即时的。使用诸如 winforms 之类的古老技术是您永远无法实现的。 Full source code on GitHub
  • 谢谢你们。我会检查您的解决方案并发布我的发现!

标签: c# winforms visual-studio


【解决方案1】:

我将为启动时所需的每个角度创建一个静态图像列表。

然后您可以在列表中引用正确的索引并使用该预旋转图像。

【讨论】:

    【解决方案2】:

    感谢您的帮助!

    我所做的是在我的 Winforms 项目中添加了一个 WPF 控件。 WPF 控件包含两个图像,并且仅包含用于旋转正面图像的代码:

    C#:

        public void Rotate(float deg)
        {
            RotateTransform rt = new RotateTransform(deg);
    
            image2.RenderTransform = rt;
        }
    

    XAML:

    <UserControl x:Class="Project.WPF_imgControl"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             mc:Ignorable="d" 
             d:DesignHeight="300" d:DesignWidth="300">
    <Grid>
        <Image HorizontalAlignment="Left" Name="image1" Stretch="Fill" VerticalAlignment="Top"  Source="/Project;component/Images/Tachometer.png" />
        <Image RenderTransformOrigin =".5,.5" HorizontalAlignment="Left" Name="image2" Stretch="Fill" VerticalAlignment="Top" Source="/Project;component/Images/Pointer_80%25.png" />
    </Grid>
    

    现在我只需要找出为什么图像在 WPF 组件中与我的标准表单相比看起来参差不齐。

    编辑:RenderOptions.BitmapScalingMode="HighQuality" 成功了 :)

    //乔克

    【讨论】:

      猜你喜欢
      • 2022-06-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-02-23
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多