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

Has*_*tor 2 c# image-processing threadpool windows-phone-8

我需要在WP8上创建一个图像的缩略图,目前我正面临困难.简而言之,我知道这样做的唯一方法就是使用类System.Windows.Controls.Image,System.Windows.Media.Imaging.BitmapImage而且System.Windows.Media.Imaging.WritableBitmap.我也试图在线程池上执行缩略图创建,因为它是在线程池上运行的其他更大操作的一部分.

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

那么有没有其他方法从图像流创建缩略图(我从PhotoChooser任务获得它)?也许一些其他API,不需要那些UI绑定类?试图把它搞定,甚至谷歌它,但没有运气.

Has*_*tor 5

好吧,我想我也会在这里提出自己的答案,因为它从一些不同的角度展示了一些东西.贾斯汀·安吉尔的回答是可以的,但是有一些问题:

  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;
        }
Run Code Online (Sandbox Code Playgroud)

我正在捕获UI线程的SynchronizationContext,而我仍处于UI线程(在View Model中),并进一步传递,然后我使用闭包来捕获局部变量,以便它们可用于运行的回调在UI线程上.我也使用锁和监视器来同步这两个线程并等待映像准备好.

如果有的话,我会根据投票接受我或Justin Angel的回答.:)

编辑:你可以得到分派器的实例SynchronizationContext通过System.Threading.SynchronizationContext.Current,而你在UI线程(在按钮单击处理程序,例如).像这样:

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);
    }
Run Code Online (Sandbox Code Playgroud)

  • 好吧,既然我收到了另一个downvote,我再问一次:这种方法究竟是什么问题?为什么它不是一个好的方法依赖?如果你贬低但没有指出答案的缺点,那只是浪费你的时间(虽然我明白用鼠标点击一下比写一些有意义的东西更容易). (3认同)