C# 当图像宽度超过40000时,渲染图像失败

C# 当图像宽度超过40000时,渲染图像失败,c#,wpf,image,bitmap,png,C#,Wpf,Image,Bitmap,Png,我正在尝试创建一个类似于Adobe Premiere的时间刻度,如下所示: 但是,我必须降到0.01秒的增量 我的时间线控件看起来像: 更新: 我使用了@Sten Petrov建议,并使用了可视化画笔 但是现在我被困在如何实现标签的问题上 我的新代码(包含可以更改的控件): 下面是创建DrawingVisual的 private List<DrawingVisual> generateHeaderVisualChunks(int width, int chunkSize, Time

我正在尝试创建一个类似于Adobe Premiere的时间刻度,如下所示: 但是,我必须降到0.01秒的增量

我的时间线控件看起来像:

更新: 我使用了@Sten Petrov建议,并使用了
可视化画笔

但是现在我被困在如何实现
标签的问题上

我的新代码(包含可以更改的控件):

下面是创建
DrawingVisual的

private List<DrawingVisual> generateHeaderVisualChunks(int width, int chunkSize, TimelineViewModel.ViewLevel level) 
{ 
    var ret = new List<DrawingVisual>();

    var currentTime = new TimeSpan(0, 0, 0, 0, 0);
    var timeStep = new TimeSpan(0, 0, 0, 0, (int)level);
    var currentLine = 0;

    const double DistanceBetweenLines = 5;
    const int TenthOfSecondLine = 10;
    const int SecondLine = 100;

    int iterations = (width / chunkSize);
    int remainder = width % chunkSize; //not doing anything with yet

    var grayBrush = new SolidColorBrush(Color.FromRgb(192, 192, 192));
    var grayPen = new Pen(grayBrush, 2);
    var whitePen = new Pen(Brushes.Purple, 2);

    grayBrush.Freeze();
    grayPen.Freeze();
    whitePen.Freeze();

    for (int i = 0; i < iterations; i++)
    {
        var visual = new DrawingVisual();
        using (var dc = visual.RenderOpen())
        {
            double currentX = 0;

            if (i > 0)
            {
                currentLine--;
                currentTime -= timeStep;
            }

            while (currentX <= chunkSize)
            {
                if (((currentLine % SecondLine) == 0) && currentLine != 0)
                {
                    dc.DrawLine(whitePen, new Point(currentX, 30), new Point(currentX, 15));
                    FormattedText text = null;
                    double tempX = currentX;
                    switch (level)
                    {
                        case TimelineViewModel.ViewLevel.Level1:
                            text = new FormattedText(
                                    currentTime.ToString(@"hh\:mm\:ss\.fff"),
                                    CultureInfo.CurrentCulture,
                                    FlowDirection.LeftToRight,
                                    new Typeface("Tahoma"),
                                    8,
                                    grayBrush);
                            break;
                    }

                    dc.DrawText(text, new Point((tempX - 22), 0));
                }
                else if ((((currentLine % TenthOfSecondLine) == 0) && currentLine != 0)
                            && (currentLine % SecondLine) != 0)
                {
                    dc.DrawLine(grayPen, new Point(currentX, 30), new Point(currentX, 20));

                    FormattedText text = null;
                    switch (level)
                    {
                        case TimelineViewModel.ViewLevel.Level1:
                            text = new FormattedText(
                                    string.Format(".{0}", currentTime.Milliseconds),
                                    CultureInfo.CurrentCulture,
                                    FlowDirection.LeftToRight,
                                    new Typeface("Tahoma"),
                                    8,
                                    grayBrush);
                            break;
                    }

                    dc.DrawText(text, new Point((currentX - 8), 8));
                }
                else
                {
                    dc.DrawLine(grayPen, new Point(currentX, 30), new Point(currentX, 25));
                }

                currentX += DistanceBetweenLines;
                currentLine++;
                currentTime += timeStep;
            }
        }
        ret.Add(visual);
    }
    return ret;
}
我的所有png片段都在各自的文件中正确呈现。 我的时间轴被正确渲染,直到我超过1:20,然后事情就破裂了

见:

就像图像被弄脏了一样。 有人知道这是怎么回事吗


谢谢

我在等有一天我的客厅会有一台水平分辨率的电视,这需要像你这样的方法

放弃你刚才演示的这整段代码,它永远不会变得可用和可维护,你只能设法从中挤出一段

然后了解,有很多教程,它可以重复您的视觉模板,不需要PNG,并且在屏幕分辨率改变时(高达40001px宽)可以更好地扩展


对于出现在标记上方的数字,有一百万种不同的方法,其中一些方法可以与上面提到的可视笔刷组合,例如表示时间线单位(两个较大标记之间的空间)的用户控件。现在,将其中几个放置在网格中(stackpanel、canvas…如您所愿),并(动态地)调整它们的偏移量和标签-突然之间,您可以在屏幕上用10个控件表示无限时间线。

“我的假设错了吗?”是的。“嗯,它不够轻。”为什么不够轻?也许您应该考虑虚拟化,只绘制可见的内容,而不是所有内容。不知道你以前是怎么做的,我们无法修复它。但是图像这件事绝对不是轻量级的。我以前使用过一个
VisualHost
,我会使用generateHeadServiceSualChunks,但不会对它进行分块。它不够亮,因为
VisualHost
会导致我的视频跳过。我的
ScrollViewer
将当前视频位置保持在
ScrollViewer
的中心位置,因此
ScrollViewer
每10毫秒滚动一次。我注意到在GenerateHeadServicesHunk中,您的笔、画笔和字体应该在函数之外创建,可能是静态的,不需要通过创建新的GC来给GC施加压力,它们不会改变。我还想知道你是否可以在你的视觉中生成一个比可见的更大的图像,允许scrollviewer移动它,在接近尾声时检测,然后重新生成。换句话说,把它分成更大的块,这样你就不会经常做繁重的工作。即使是Jumbotron也有40000像素的宽度吗?你为什么要在每个像素中压缩这么多信息?猜猜Pieter的意思是,不管你的时间轴分辨率如何,平均屏幕最多只能显示2000个像素。您需要生成可见部分,而不是整个时间线。
public void RenderHeaderPicture()
{
    const int ChunkSize = 5000;
    var bitmapFrames = new List<BitmapFrame>();

    // generates X number of DrawingVisual's based on ChunkSize
    List<DrawingVisual> visuals = generateHeaderVisualChunks(
        AppViewModel.TimelineViewModel.HeaderWidth, ChunkSize, TimelineViewModel.ViewLevel.Level1);

    for (var i = 0; i < visuals.Count; i++)
    {
        var renderTargetBitmap = new RenderTargetBitmap(ChunkSize, 30, 96, 96, PixelFormats.Pbgra32);
        renderTargetBitmap.Render(visuals[i]);

        //test to make sure image good
        saveHeaderSegmentAsPng(string.Format("headerSeg{0}.png", i), renderTargetBitmap);

        bitmapFrames.Add(BitmapFrame.Create(renderTargetBitmap));
    }

    // put the frames back together now
    var drawingVisual = new DrawingVisual();
    using (var drawingContext = drawingVisual.RenderOpen())
    {
        for (int i = 0; i < bitmapFrames.Count; i++)
        {
            drawingContext.DrawImage(bitmapFrames[i], new Rect(i * ChunkSize, 0, bitmapFrames[i].PixelWidth, 30));
        }
        drawingContext.Close();
    }

    var newBmp = new RenderTargetBitmap(AppViewModel.TimelineViewModel.HeaderWidth, 30, 96, 96, PixelFormats.Pbgra32);
    newBmp.Render(drawingVisual);

    AppViewModel.TimelineViewModel.HeaderImageSource = newBmp;
}
private List<DrawingVisual> generateHeaderVisualChunks(int width, int chunkSize, TimelineViewModel.ViewLevel level) 
{ 
    var ret = new List<DrawingVisual>();

    var currentTime = new TimeSpan(0, 0, 0, 0, 0);
    var timeStep = new TimeSpan(0, 0, 0, 0, (int)level);
    var currentLine = 0;

    const double DistanceBetweenLines = 5;
    const int TenthOfSecondLine = 10;
    const int SecondLine = 100;

    int iterations = (width / chunkSize);
    int remainder = width % chunkSize; //not doing anything with yet

    var grayBrush = new SolidColorBrush(Color.FromRgb(192, 192, 192));
    var grayPen = new Pen(grayBrush, 2);
    var whitePen = new Pen(Brushes.Purple, 2);

    grayBrush.Freeze();
    grayPen.Freeze();
    whitePen.Freeze();

    for (int i = 0; i < iterations; i++)
    {
        var visual = new DrawingVisual();
        using (var dc = visual.RenderOpen())
        {
            double currentX = 0;

            if (i > 0)
            {
                currentLine--;
                currentTime -= timeStep;
            }

            while (currentX <= chunkSize)
            {
                if (((currentLine % SecondLine) == 0) && currentLine != 0)
                {
                    dc.DrawLine(whitePen, new Point(currentX, 30), new Point(currentX, 15));
                    FormattedText text = null;
                    double tempX = currentX;
                    switch (level)
                    {
                        case TimelineViewModel.ViewLevel.Level1:
                            text = new FormattedText(
                                    currentTime.ToString(@"hh\:mm\:ss\.fff"),
                                    CultureInfo.CurrentCulture,
                                    FlowDirection.LeftToRight,
                                    new Typeface("Tahoma"),
                                    8,
                                    grayBrush);
                            break;
                    }

                    dc.DrawText(text, new Point((tempX - 22), 0));
                }
                else if ((((currentLine % TenthOfSecondLine) == 0) && currentLine != 0)
                            && (currentLine % SecondLine) != 0)
                {
                    dc.DrawLine(grayPen, new Point(currentX, 30), new Point(currentX, 20));

                    FormattedText text = null;
                    switch (level)
                    {
                        case TimelineViewModel.ViewLevel.Level1:
                            text = new FormattedText(
                                    string.Format(".{0}", currentTime.Milliseconds),
                                    CultureInfo.CurrentCulture,
                                    FlowDirection.LeftToRight,
                                    new Typeface("Tahoma"),
                                    8,
                                    grayBrush);
                            break;
                    }

                    dc.DrawText(text, new Point((currentX - 8), 8));
                }
                else
                {
                    dc.DrawLine(grayPen, new Point(currentX, 30), new Point(currentX, 25));
                }

                currentX += DistanceBetweenLines;
                currentLine++;
                currentTime += timeStep;
            }
        }
        ret.Add(visual);
    }
    return ret;
}
private static void saveHeaderSegmentAsPng(string fileName, RenderTargetBitmap renderTargetBitmap)
{
    var pngBitmapEncoder = new PngBitmapEncoder();

    pngBitmapEncoder.Frames.Add(BitmapFrame.Create(renderTargetBitmap));
    using (var fileStream = new FileStream(fileName, FileMode.Create))
    {
        pngBitmapEncoder.Save(fileStream);
        fileStream.Flush();
        fileStream.Close();
    }
}