2011-05-14 79 views
18

如何显示从网址加载的渐进式JPEG?我试图在WPF中的图像控件中显示Google Maps图像,但我希望保持图像作为渐进式JPG的优势。如何在WPF中显示渐进式JPEG?

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

Image imgMap; 
BitmapImage mapLoader = new BitmapImage(); 

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

imgMap.Source = mapLoader; 

目前,我与此有关。它只会在完全加载后显示图像。我想逐步展示它。

回答

12

一个非常基本的样本。我确定有优化的空间,你可以做一个独立的课程,可以处理大量的请求,但至少可以工作,并且可以根据需要进行调整。另外请注意,每次我们报告进度时,此示例都会创建一个图像,您应该避免它!做一个约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; 
} 
+0

@ TFD我建议你谷歌Progressive JPeG。有很多关于它是什么的资源,但并不真正如何处理它们......特别是在.net中。在.NET中编写一个并不是一项简单的任务。 – Mel 2011-05-26 16:20:24

+4

虽然我真的跳过了进步的事情,但我会尽力为自己辩护一下:基本问题是只有在下载完成时才显示图像,并且OP要在显示图像的同时显示部分内容。我的示例代码做到了。它是对问题的解决方案,也是对上面你可以阅读的愿望的回答:“我想逐步展示它。”虽然没有答案__问题,问题解决了:最终用户可以看到下载图像的一些进展。也许我不配让OP接受我的回答,但我不配得到-1。 – Ben 2011-05-26 17:47:48

+0

@BoltClock我知道什么是渐进式JPG。什么进度条?此示例代码使用“进度:{0}”显示图像和百分比完成计数器。给出部分流可用的图像显示。 OP就是这么想的?唯一的错误是不链接到一个样本的渐进JPG。将URL更改为大型渐进式,您可以看到它工作正常。请不要-1在技术上是正确的职位 – TFD 2011-05-26 23:30:27

1

这似乎是图像控制的缺点。也许你可以创建一个从Image继承的StreamImage,在构造函数中获取一个流,从流中读取背景中的字节,计算出足够多的时间,构造一个内部“模糊图像”,其中包含读取的字节,并呈现出迭代直到它具有所有的字节。你必须了解逐行JPEG的字节是如何发送的 - 我不认为它很简单。

+0

@Patrick_Szalapski我不明白你的评论来自何处。 WPF图像完全支持渐进式jpg。没有短缺。它只是工作。请参阅@ben的回答 – TFD 2011-05-27 00:10:40

+1

@TFD这仍然是每5%重新渲染部分图像。完全支持将在加载时一次性渲染。 – Rup 2011-05-27 10:44:29

+0

对 - 我在想,一个自定义控件将@ Ben的所有代码都嵌入到控件中。如果你从外部处理这些代码,那么当然你不需要特殊的控制。 – 2011-05-31 22:35:48