如何在 WPF 中显示渐进式 JPEG?

发布于 2024-11-07 20:21:44 字数 350 浏览 0 评论 0原文

如何在从 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;

目前我就靠这个凑合着。它只会在完全加载后显示图像。我想逐步展示它。

How to display a progressive JPEG as it loads from a web URL? I am trying to display a Google Maps image in a image control in WPF, but I want to keep the advantage of the image being a progressive JPG.

How to load a progressive JPG in WPF?

Image imgMap;
BitmapImage mapLoader = new BitmapImage();

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

imgMap.Source = mapLoader;

Currently, I make do with this. It will only shows the image after it loads completely. I want to show it progressively.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

不甘平庸 2024-11-14 20:21:44

一个非常基本的示例。我确信还有优化的空间,您可以从中创建一个单独的类来处理大量请求,但至少它可以工作,并且您可以根据您的需要塑造它。另请注意,每次我们报告进度时,此示例都会创建一个图像,您应该避免它!大约每 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;
}

A very basic sample. Im sure there are room for optimizations, and you can do a separate class from it that can handle numerous request, but at least its working, and you can shape it for your needs. Also note that this sample creates an image every time that we report a progress, you should avoid it! Do an image about every 5% or so to avoid a big overhead.

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>

Code-behind:

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;
}
琴流音 2024-11-14 20:21:44

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

This does seem to be a shortcoming of the Image control. Maybe you could create a StreamImage that inherits from Image, takes a stream in the constructor, reads bytes in the background from the stream, figures out when it has enough, constructs an internal "fuzzy image" with the bytes read so far and renders that iteratively until it has all the bytes. You'd have to understand how bytes of a progressive JPEG are sent--I don't imagine it is simple.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文