【问题标题】:How can I display a Progressive JPEG in WPF?如何在 WPF 中显示渐进式 JPEG?
【发布时间】:2011-08-25 09:44:49
【问题描述】:

如何在从 Web URL 加载时显示渐进式 JPEG?我正在尝试在 WPF 中的图像控件中显示 Google 地图图像,但我想保持图像是渐进式 JPG 的优势。

如何在 WPF 中加载渐进式 JPG?

Image imgMap;
BitmapImage mapLoader = new BitmapImage();

mapLoader.BeginInit();
mapLoader.UriSource = new Uri(URL);
mapLoader.EndInit();

imgMap.Source = mapLoader;

目前,我正在解决这个问题。它只会在完全加载后显示图像。我想逐步展示它。

【问题讨论】:

    标签: wpf image bitmapimage


    【解决方案1】:

    一个非常基本的示例。我确信有优化的空间,你可以从它做一个单独的类来处理大量的请求,但至少它可以工作,你可以根据你的需要来塑造它。另请注意,每次我们报告进度时,此示例都会创建一个图像,您应该避免它!大约每 5% 左右做一张图片,以避免很大的开销。

    Xaml:

    <Window x:Class="ScrollViewerTest.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525"
        DataContext="{Binding RelativeSource={RelativeSource Self}}">
      <StackPanel>
        <TextBlock Text="{Binding Path=Progress, StringFormat=Progress: {0}}" />
        <Image Source="{Binding Path=Image}" />
      </StackPanel>
    </Window>
    

    代码隐藏:

    public partial class MainWindow : Window, INotifyPropertyChanged
    {
    
      #region Public Properties
    
      private int _progress;
      public int Progress
      {
        get { return _progress; }
        set
        {
          if (_progress != value)
          {
            _progress = value;
    
            if (PropertyChanged != null)
              PropertyChanged(this, new PropertyChangedEventArgs("Progress"));
          }
        }
      }
    
      private BitmapImage image;
      public BitmapImage Image
      {
        get { return image; }
        set
        {
          if (image != value)
          {
            image = value;
            if (PropertyChanged != null)
              PropertyChanged(this, new PropertyChangedEventArgs("Image"));
          }
        }
      }
    
      #endregion
    
      BackgroundWorker worker = new BackgroundWorker();
    
      public MainWindow()
      {
        InitializeComponent();
    
        worker.DoWork += backgroundWorker1_DoWork;
        worker.ProgressChanged += new ProgressChangedEventHandler(worker_ProgressChanged);
        worker.WorkerReportsProgress = true;
        worker.RunWorkerAsync(@"http://Tools.CentralShooters.co.nz/Images/ProgressiveSample1.jpg");
      }
    
      // This function is based on code from
      //   http://devtoolshed.com/content/c-download-file-progress-bar
      private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
      {
        // the URL to download the file from
        string sUrlToReadFileFrom = e.Argument as string;
    
        // first, we need to get the exact size (in bytes) of the file we are downloading
        Uri url = new Uri(sUrlToReadFileFrom);
        System.Net.HttpWebRequest request = (System.Net.HttpWebRequest)System.Net.WebRequest.Create(url);
        System.Net.HttpWebResponse response = (System.Net.HttpWebResponse)request.GetResponse();
        response.Close();
        // gets the size of the file in bytes
        Int64 iSize = response.ContentLength;
    
        // keeps track of the total bytes downloaded so we can update the progress bar
        Int64 iRunningByteTotal = 0;
    
        // use the webclient object to download the file
        using (System.Net.WebClient client = new System.Net.WebClient())
        {
          // open the file at the remote URL for reading
          using (System.IO.Stream streamRemote = client.OpenRead(new Uri(sUrlToReadFileFrom)))
          {
            using (Stream streamLocal = new MemoryStream((int)iSize))
            {
              // loop the stream and get the file into the byte buffer
              int iByteSize = 0;
              byte[] byteBuffer = new byte[iSize];
              while ((iByteSize = streamRemote.Read(byteBuffer, 0, byteBuffer.Length)) > 0)
              {
                // write the bytes to the file system at the file path specified
                streamLocal.Write(byteBuffer, 0, iByteSize);
                iRunningByteTotal += iByteSize;
    
                // calculate the progress out of a base "100"
                double dIndex = (double)(iRunningByteTotal);
                double dTotal = (double)byteBuffer.Length;
                double dProgressPercentage = (dIndex / dTotal);
                int iProgressPercentage = (int)(dProgressPercentage * 100);
    
                // update the progress bar, and we pass our MemoryStream, 
                //  so we can use it in the progress changed event handler
                worker.ReportProgress(iProgressPercentage, streamLocal);
              }
    
              // clean up the file stream
              streamLocal.Close();
            }
    
            // close the connection to the remote server
            streamRemote.Close();
          }
        }
      }
    
      void worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
      {
        Dispatcher.BeginInvoke(
             System.Windows.Threading.DispatcherPriority.Normal,
             new Action(delegate()
             {
               MemoryStream stream = e.UserState as MemoryStream;
    
               BitmapImage bi = new BitmapImage();
               bi.BeginInit();
               bi.StreamSource = new MemoryStream(stream.ToArray());
               bi.EndInit();
    
               this.Progress = e.ProgressPercentage;
               this.Image = bi;
             }
           ));
      }
    
      public event PropertyChangedEventHandler PropertyChanged;
    }
    

    【讨论】:

    • @TFD 我建议你谷歌渐进式 JPeG。有很多关于它是什么的资源.. 但不是真正如何处理它们.. 尤其是在 .net 中.. 在 .net 中编写一个不是一项简单的任务。
    • 虽然我真的跳过了渐进式的事情,但我会尝试为自己辩护一点:基本问题是图像仅在下载完成时显示,并且 OP 想要显示图像的一部分在下载它时。我的示例代码做到了。它是对问题的解决方案,也是对您可以在上面阅读的愿望的回答:I want to show it progressively.。虽然问题没有答案,但问题解决了:最终用户可以看到下载图像的一些进度。也许我不值得 OP 接受我的回答,但我也不值得 -1。
    • @BoltClock 我知道渐进式 JPG 是什么。什么进度条?此示例代码使用“进度:{0}”显示图像和完成百分比计数器。图像显示给定的部分流可用。这就是OP想要的吗?唯一的错误是没有链接到示例渐进式 JPG。将 URL 更改为大渐进式,您可以看到它工作正常。请不要 -1 技术上正确的帖子
    • 我的错是误读了代码和无端霸道。是的,我应该自己测试一下。我很抱歉。我没有要撤销的反对票,但这里实际上是正确的赞成票。 (还删除了我以前的 cmets。)
    • @Ben 你现在已经摆脱困境了,希望你能享受到赏金!
    【解决方案2】:

    这似乎是 Image 控件的一个缺点。也许您可以创建一个从 Image 继承的 StreamImage,在构造函数中获取一个流,从流中读取背景中的字节,确定它何时有足够的内容,使用到目前为止读取的字节构造一个内部“模糊图像”并呈现迭代直到它拥有所有字节。您必须了解渐进式 JPEG 的字节是如何发送的——我认为这并不简单。

    【讨论】:

    • @Patrick_Szalapski 我不明白你的评论来自哪里。 WPF Image 完全支持渐进式 jpg。没有缺点。它只是工作。查看@ben 的回答
    • @TFD 仍然每 5% 从头重新渲染部分图像。完全支持将在加载时一次性渲染它。
    • 对--我在想一个自定义控件,其中包含 @Ben 的所有代码。如果您在外部处理该代码,那么您当然不需要特殊控件。
    猜你喜欢
    • 2011-06-12
    • 2012-06-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-06-03
    • 1970-01-01
    • 2016-01-14
    相关资源
    最近更新 更多