正在线程池中创建Windows Phone 8上的图像缩略图

本文关键字:图像 略图 Phone Windows 线程 创建 | 更新日期: 2023-09-27 17:58:52

我需要在WP8上创建一个图像的缩略图,目前我面临着困难。简而言之,我知道实现这一点的唯一方法,即使用类System.Windows.Controls.ImageSystem.Windows.Media.Imaging.BitmapImageSystem.Windows.Media.Imaging.WritableBitmap。我还试图在线程池上执行缩略图创建,因为这是在线程池中运行的其他更大操作的一部分。

正如您可能已经理解的那样,即使在尝试创建上述类的实例时,我也会因无效的跨线程访问而失败。真的很遗憾,因为这个缩略图甚至不会在UI中使用,只会保存到一个文件中,然后从文件中显示。我的工作与UI线程无关,我仍然面临着这种限制。

那么,有没有其他方法可以从图像流中创建缩略图(我是从PhotoChooser任务中获得的)?也许是其他API,它不需要这些UI-bound类?我试着用bing,甚至用谷歌搜索,但没有成功。

正在线程池中创建Windows Phone 8上的图像缩略图

好吧,我想我也会在这里给出我自己的答案,因为它从一个不同的角度展示了事情。贾斯汀·安吉尔的答案是可以的,但有几个问题:

  1. 当代码深入模型层并在后台线程上运行时,不可能引用Dispatcher
  2. 我需要从该方法返回缩略图,然后在同一同步上下文中使用它。否则,我将不得不围绕这种创建缩略图的方法更改很多代码

考虑到这些要求,以下是我的解决方案:

private WriteableBitmap CreateThumbnail(Stream stream, int width, int height, SynchronizationContext uiThread)
        {
            // This hack comes from the problem that classes like BitmapImage, WritableBitmap, Image used here could 
            // only be created or accessed from the UI thread. And now this code called from the threadpool. To avoid
            // cross-thread access exceptions, I dispatch the code back to the UI thread, waiting for it to complete
            // using the Monitor and a lock object, and then return the value from the method. Quite hacky, but the only
            // way to make this work currently. It's quite stupid that MS didn't provide any classes to do image 
            // processing on the non-UI threads.
            WriteableBitmap result = null;
            var waitHandle = new object();
            lock (waitHandle)
            {
                uiThread.Post(_ => 
                {
                    lock (waitHandle)
                    {
                        var bi = new BitmapImage();
                        bi.SetSource(stream);
                        int w, h;
                        double ws = (double)width / bi.PixelWidth;
                        double hs = (double)height / bi.PixelHeight;
                        double scale = (ws > hs) ? ws : hs;
                        w = (int)(bi.PixelWidth * scale);
                        h = (int)(bi.PixelHeight * scale);
                        var im = new Image();
                        im.Stretch = Stretch.UniformToFill;
                        im.Source = bi;
                        result = new WriteableBitmap(width, height);
                        var tr = new CompositeTransform();
                        tr.CenterX = (ws > hs) ? 0 : (width - w) / 2;
                        tr.CenterY = (ws < hs) ? 0 : (height - h) / 2;
                        tr.ScaleX = scale;
                        tr.ScaleY = scale;
                        result.Render(im, tr);
                        result.Invalidate();
                        Monitor.Pulse(waitHandle);
                    }
                }, null);
                Monitor.Wait(waitHandle);
            }
            return result;
        }

当我还在UI线程中(在视图模型中)时,我会捕获UI线程的SynchronizationContext,并进一步传递它,然后我会使用闭包来捕获局部变量,这样它们就可以用于在UI线程上运行的回调。我还使用锁和监视器来同步这两个线程,并等待图像准备好。

如果有投票,我会接受我或贾斯汀·安吉尔的回答。:)

EDIT:在UI线程上(例如,在按钮单击处理程序中),您可以通过System.Threading.SynchronizationContext.Current获取Dispatcher的SynchronizationContext实例。像这样:

private async void CreateThumbnailButton_Clicked(object sender, EventArgs args)
    {
        SynchronizationContext uiThread = SynchronizationContext.Current;
        var result = await Task.Factory.StartNew<WriteableBitmap>(() =>
            {
                Stream img = GetOriginalImage();// get the original image through a long synchronous operation
                return CreateThumbnail(img, 163, 163, uiThread);
            });
        await SaveThumbnailAsync(result);
    }
是的,使用WriteableBitmap需要访问UI字符串。您可能必须使用Dispatcher类将UI线程上的工作安排为工作流的一部分。

我唯一能想到的另一件事是将图像保存到手机的媒体库中,并使用Picture.GetThumbnail()方法获得一个非常低分辨率的缩略图。如果不访问UI线程,它可能工作,也可能不工作。此外,一旦你将图片添加到用户的媒体库中,你就无法删除这些图片,所以要小心不要向这些文件夹发送垃圾邮件。