2013-02-13 5 views
2

WP8에서 이미지 미리보기 이미지를 만들어야하는데 현재 문제가 있습니다. 간단히 말해, 클래스 System.Windows.Controls.Image, System.Windows.Media.Imaging.BitmapImageSystem.Windows.Media.Imaging.WritableBitmap을 사용하는이 작업을 수행하는 유일한 방법은 알고 있습니다. 또한 스레드 풀에서 실행되는 다른 더 큰 작업의 일부이기 때문에 스레드 풀에서 축소판 만들기를 수행하려고합니다.Windows Phone 8의 이미지 축소판 작성 threadpool

아마도 이미 이해 했으므로 위 클래스의 인스턴스를 만들 때도 잘못된 크로스 스레드 액세스가 실패합니다. 이 축소판 그림은 UI에서 사용하지 않고 파일에만 저장하고 나중에 파일에서 표시하기 때문에 실제로는 수치 스럽습니다. 내 작품은 UI 스레드와 아무 관련이 없으며, 여전히 이러한 제약에 직면 해있다.

이미지 스트림에서 미리보기 이미지를 만드는 다른 방법이 있습니까 (PhotoChooser 작업에서 가져 오는 방법)? 어쩌면 UI 바인딩 클래스를 필요로하지 않는 다른 API일까요? 그것도 going하려고했지만, 운이 없다.

답변

5

좋아, 나는 약간의 다른 관점에서 물건을 보여주기 때문에 나는 여기에 내 자신의 대답도 넣을 것이라고 생각한다. 저스틴 천사 대답은 괜찮지 만, 그것으로 문제가 몇 가지있다 : 코드가 모델 층의 깊이와 배경 스레드에서 실행할 때

  1. 그것은, 디스패처에 대한 참조를 가질 수는 없습니다은.
  2. 메서드에서 축소판 이미지를 반환하고 나중에 동일한 동기화 컨텍스트에서 사용해야합니다. 그렇지 않으면 미리보기 이미지를 만드는 방법을 중심으로 많은 코드를 변경해야합니다.
  3. 염두에 요구 사항을

가 여기 내 솔루션입니다 :

내가 UI 스레드에 여전히 해요 동안 (보기 모델에서) UI 스레드의 SynchronizationContext에 캡처, 더욱 그것을 통과하고있어
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 스레드에있는 동안 당신은 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); 
    } 
+0

Downvoters는 왜 설명해야합니까? 그것은 작동하고 결정 론적 인 접근 방식입니다. – Haspemulator

+3

좋아, 내가 또 다른 downvote을 받았기 때문에, 나는 다시 한 번 묻고있다 : 정확히이 접근법에 잘못된 무엇입니까? 왜 의지하는 것이 좋은 방법이 아닌가요?downvote하지만 대답의 단점을 지적하지 않으면, 그것은 단지 시간 낭비 일뿐입니다 (마우스로 한 번 클릭하는 것은 의미있는 것을 작성하는 것보다 쉽습니다). – Haspemulator

+0

ViewModel을 통해이 메서드를 호출하는 방법. 어디에서 SynchronizationContext를 얻을 수 있습니다. 메소드를 호출하는 방법을 추가 할 수 있습니까? – StezPet

2

예, WriteableBitmap을 사용하려면 UI 문자열에 액세스해야합니다. Dispatcher 클래스를 사용하여 워크 플로우의 일부로 UI 스레드에서 작업을 스케줄링해야 할 수도 있습니다.

내가 생각할 수있는 유일한 다른 방법은 이미지를 Phone의 MediaLibrary에 저장하고 Picture.GetThumbnail() 메서드를 사용하여 매우 낮은 해상도의 미리보기 이미지를 얻는 것입니다. UI 스레드에 대한 액세스없이 작동하거나 작동하지 않을 수 있습니다. 또한 사용자의 MediaLibrary에 그림을 추가하면 삭제할 수 없으므로 해당 폴더를 스팸하지 않도록주의하십시오.