2014-02-21 2 views
1

MonoGame을 사용하여 Windows Phone 7 게임을 Windows Phone 8에 이식하고 있습니다. 내 게임은 인터넷에서 사진을로드하려면 Texture2D.FromStream을 사용하고, 아직 MonoGame에 구현되지 않는 한 제안 방안을 시도하고 있습니다. UI 스레드에서 BitmapImage로 그림을로드하고 있습니다.WP8 : Dispatcher.BeginInvoke가 UI 스레드에서 작업을 실행하지 않습니다.

  Texture2D result = null; 
      EventWaitHandle Wait = new AutoResetEvent(false); 
      Deployment.Current.Dispatcher.BeginInvoke(() => 
      { 
       BitmapImage image = new BitmapImage(); 
       image.SetSource(stream); 
       WriteableBitmap writeImage = new WriteableBitmap(image); 

       var index = 0; 
       var pixels = new byte[writeImage.PixelWidth * writeImage.PixelHeight * 4]; 
       for (var h = 0; h < writeImage.PixelHeight; h++) 
        for (var w = 0; w < writeImage.PixelWidth; w++) 
        { 
         var pixel = writeImage.Pixels[index++]; 
         var wIndex = w * 4 + h * writeImage.PixelWidth * 4; 

         pixels[wIndex++] = (byte)(pixel >> 16 & 0xFF); 
         pixels[wIndex++] = (byte)(pixel >> 8 & 0xFF); 
         pixels[wIndex++] = (byte)(pixel >> 0 & 0xFF); 
         pixels[wIndex++] = (byte)(pixel >> 24 & 0xFF); 
        } 

       result = new Texture2D(graphicsDevice, writeImage.PixelWidth, writeImage.PixelHeight); 
       result.SetData(pixels); 
       Wait.Set(); 
      }); 

이론적으로 모든 것이 괜찮 : 여기

는 코드입니다. 그러나 어떻게 든 액션 코드는 호출되지 않습니다. 비 사용자 작업자 스레드에서 BeginInvoke를 호출 할 때 Deployment.Current.Dispatcher가 null이 아니며 ui-thread 디스패처를 제공합니다. 게임은 VS2012 용 MonoGame 템플릿으로 만든 WP8 프로젝트입니다. 주 스레드 (게임 생성자)의 정적 변수 dispatcher 및 SynchronizationContext에 가져 와서 그 변수가 UI 스레드와 정확히 일치하는지 확인하려고 시도했지만 도움이되지 않습니다.

SynchronizationContext uiThread = Hub.SyncContext;//set in main thread (Game constructor) 
var waitHandle = new object(); 
lock (waitHandle) 
{ 
    uiThread.Post(_ => 
    { 
     lock (waitHandle) 
     { 

      BitmapImage image = new BitmapImage(); 
      image.SetSource(stream); 
      WriteableBitmap writeImage = new WriteableBitmap(image); 

      var index = 0; 
      var pixels = new byte[writeImage.PixelWidth * writeImage.PixelHeight * 4]; 
      for (var h = 0; h < writeImage.PixelHeight; h++) 
       for (var w = 0; w < writeImage.PixelWidth; w++) 
       { 
        var pixel = writeImage.Pixels[index++]; 
        var wIndex = w * 4 + h * writeImage.PixelWidth * 4; 

        pixels[wIndex++] = (byte)(pixel >> 16 & 0xFF); 
        pixels[wIndex++] = (byte)(pixel >> 8 & 0xFF); 
        pixels[wIndex++] = (byte)(pixel >> 0 & 0xFF); 
        pixels[wIndex++] = (byte)(pixel >> 24 & 0xFF); 
       } 

      result = new Texture2D(graphicsDevice, writeImage.PixelWidth, writeImage.PixelHeight); 
      result.SetData(pixels); 

      Monitor.Pulse(waitHandle); 
     } 

    }, null); 

    Monitor.Wait(waitHandle); 
} 

그러나 다시 행운 : 실행이 발사되지 않습니다 액션 코드의 대기 및 중단 점에서 중지

또한 나는 그런 코드를 시도했다.

업데이트 : 샘플 코드 MonoGame 프로젝트에 단 하나의 로그로 간단한 코드를 추가했습니다. AutoResetEvent를 기다리는 경우 로그가 표시되지 않습니다. 내가 are.WaitOne() 작업을 제거하면 작동하기 시작했다. 나는 그것들로 바꿨다. 와이토 원 (1000)과 예상대로 일하기 시작한다. (왜?). 이미지 로딩과 관련하여 여전히 운이 없습니다. 대기중인 동안 메인 스레드가 실행되는 것을 막아주는 것 같습니다 .WaitOne().

+1

UI 스레드 (예 :로드 또는 시작 등)에서 Deployment.Current.Dispatcher 값을 가져 와서 저장하면 차이가 있습니까? –

+0

@Tertium, 왜'Post'와'Monitor.Pulse'를 쓰는 대신'uiThread.Send'를 사용하지 않으시겠습니까? – Noseratio

+0

@PeterRitchie 아니, 이건 도움이 안된다 – Tertium

답변

3

이 라이브러리를 사용하여 문제가 해결되었습니다. https://github.com/Taggersoft/ImageTools 이 포크는 WP8 용이며 정확히 Contract.Requires 문을 모두 삭제하면 매우 효과적입니다.

 public static Texture2D TextureFromStream(GraphicsDevice graphicsDevice, Stream stream, String dbg_nm) 
    {   
     #if WP8 
     byte [] b = new byte[4]; 
     stream.Read(b, 0, 4); 
     stream.Seek(0, SeekOrigin.Begin); 
     int stream_type = getStreamType(b, 0); 
     Texture2D result = null; 

     if (stream_type == 1 || stream_type == 0) 
     { 
      var image = new ExtendedImage(); 
      if (stream_type == 0) 
       new JpegDecoderNano().Decode(image, stream); 
      else 
      { 
       try 
       { 
        new PngDecoder().Decode(image, stream); 
       } 
       catch (System.Exception ex) 
       { 
        Debug.WriteLine("Error reading " + dbg_nm + ":" + ex.Message); 
       }     
      } 

      var Colors = new Color[image.Pixels.Length/4]; 
      for (var i = 0; i < image.Pixels.Length/4; i++) 
      { 
       Color unpackedColor = new Color(); 
       unpackedColor.R = (byte)(image.Pixels[i * 4 + 0]); 
       unpackedColor.G = (byte)(image.Pixels[i * 4 + 1]); 
       unpackedColor.B = (byte)(image.Pixels[i * 4 + 2]); 
       unpackedColor.A = (byte)(image.Pixels[i * 4 + 3]); 
       Colors[i] = unpackedColor; 
      } 
      Texture2D texture2D = new Texture2D(graphicsDevice, image.PixelWidth, image.PixelHeight); 
      texture2D.SetData(Colors); 
      result = texture2D; 
     } 

     return result; 

     #else 
     return Texture2D.FromStream(graphicsDevice, stream); 
     #endif 
    } 

그리고 게임 생성자 :

#if WP8 
    Decoders.AddDecoder<PngDecoder>(); 
    Decoders.AddDecoder<JpegDecoder>(); 
    #endif 

그것은 actualy 해결의 여기 내 Texture2d.FromStream 교체입니다. 하지만 제 경우에는이 방법은 MS BitmapImage를 uithread에서 사용하는 것보다 훨씬 낫습니다.

관련 문제