2014-12-10 9 views
22

Nie spodziewałemJak prawidłowo renderować duże bitmapy w WPF?

RenderTargetBitmap.Render(visual) 

mieć żadnych skutków ubocznych fragment Zmiana danych bitmapy siebie. Wygląda na to, że to nieprawda. Nie mogę powtórzyć tego więcej niż 60 razy, zanim zaczną się pojawiać niektóre brzydkie artefakty renderowania.

Jak poprawnie renderować wiele ikonek w WPF? Poniżej znajduje się kod służący do odtworzenia problemu.

wygenerować ikonek w ten sposób:

BitmapSource Sprite() 
    { 
     var bitmap = new RenderTargetBitmap(
      500, 500, 
      96, 96, 
      PixelFormats.Default); 

     var visual = new DrawingVisual(); 
     var rect = new Rect(
        new Size(
         bitmap.Width, 
         bitmap.Height)); 

     using (DrawingContext context = visual.RenderOpen()) 
      context.DrawLine(
       new Pen(Brushes.Red, 100), 
       rect.TopLeft, 
       rect.BottomRight); 

     bitmap.Render(visual); 
     bitmap.Freeze(); 
     return bitmap; 
    } 

Oto płótno do renderowania wielu z nich:

public BitmapSource Canvas 
    { 
     get 
     { 
      var bitmap = new RenderTargetBitmap(
       1980, 1080, 
       96, 96, 
       PixelFormats.Default); 

      var tiles = 70; 
      for (int i = 0; i < tiles; i++) 
      { 
       var visual = new DrawingVisual(); 
       var rect = new Rect(
        bitmap.Width/tiles * i, 
        0, 
        bitmap.Width/tiles, 
        bitmap.Height); 

       using (DrawingContext context = visual.RenderOpen()) 
        context.DrawImage(Sprite(), rect); 

       bitmap.Render(visual); 
      } 

      bitmap.Freeze(); 
      return bitmap; 
     } 
    } 

widzę ten dziwny obraz będąc danych związanych z własnością płótnie ...

enter image description here

+0

Dlaczego nie wyrenderować tych 70 płytek w jednym rysunku DrawingVisual, a następnie wywołać bitmap.Render tylko raz? Trzeba po prostu przenieść pętlę 'for' do bloku' using (DrawingContext ...) '. Można również całkowicie zapisać "wewnętrzne" RenderTargetBitmaps, rysując linie bezpośrednio do "zewnętrznego" kontekstu rysunku. – Clemens

+0

Nie mogę renderować kafelków bezpośrednio, ponieważ oznacza to, że pojedynczy wzorzec będzie przechowywać odniesienia do 70 bitmap lub więcej w tym samym czasie. Skończy mi się pamięć. Niestety, potrzebuję "wewnętrznych" RenderTargetBitmaps, ponieważ płytki są danymi wejściowymi rastra dla mojej aplikacji. –

+0

1) Nie mogę odtworzyć twojego problemu, nawet z 'tiles = 2000'. 2) Mogę odtworzyć wyjątek poza pamięcią, zachowując wszystkie odniesienia do ikonek w jednym obrazie - ale w około 855, a nie w 70. Jakiego sprzętu graficznego używasz? – dbc

Odpowiedz

4

Oto przykład dla nas ing InteropBitmap:

public InteropBitmapHelper(ColorSpace colorSpace, int bpp, int width, int height, uint byteCount) 
     { 
      _currentColorSpace = colorSpace; 
      _pixelFormat = GetPixelFormat(colorSpace, bpp); 

      if (_pixelFormat == PixelFormats.Rgb24 || _pixelFormat == PixelFormats.Rgb48 || _pixelFormat == PixelFormats.Bgr32 || 
       _pixelFormat == PixelFormats.Bgr24 || _pixelFormat == PixelFormats.Bgr565 || 
       _pixelFormat == PixelFormats.Gray16 || _pixelFormat == PixelFormats.Gray8) 
      { 
       int strideWidth = (width % 4 == 0) ? width : width - width % 4 + 4; 
       if (byteCount != strideWidth * height * (_pixelFormat.BitsPerPixel/8)) 
       { 
        strideWidth = width; 
       } 
       _stride = strideWidth * _pixelFormat.BitsPerPixel/8; 

       _byteCount = (uint)((_stride) * height * ((short)bpp).NumberOfBytes()); 

       ColorFileMapping = CreateFileMapping(new IntPtr(-1), IntPtr.Zero, 0x04, 0, _byteCount, null); 

       if (ColorFileMapping == IntPtr.Zero) 
       { 
        var res=GetLastError(); 
        IPDevLoggerWrapper.Error("Could not generate InteropBitmap "+res); 
        return; 
       } 
       ViewerImageData = MapViewOfFile(ColorFileMapping, 0xF001F, 0, 0, _byteCount); 
       InteropBitmap = Imaging.CreateBitmapSourceFromMemorySection(ColorFileMapping, 
                      width, 
                      height, 
                      _pixelFormat, 
                      _stride, 
                      0) as InteropBitmap; 
      } 
      else 
      { 
       LoggerWrapper.Error("The image format is not supported"); 
       return; 
      } 
     } 

Oto jak podłączyć go do XAML.

<Canvas  
    x:Name="content" 
    Width="{Binding ElementName=MainImage, Path=ActualWidth}" 
    Height="{Binding ElementName=MainImage, Path=ActualHeight}" 
    Background="Transparent" 
    MouseEnter="ImageMouseEnter" 
    MouseLeave="ImageMouseLeave" 
    RenderTransformOrigin ="0.5,0.5"> 
    <Image x:Name="MainImage" Source="{Binding Source}" RenderOptions.BitmapScalingMode="{Binding ViewerRenderingMode }"/> 

Proszę dać mi znać, jeśli potrzebujesz więcej informacji.

+2

Właściwie nie mam żadnych trwałych obrazów o rozsądnych rozmiarach. Muszę połączyć wiele duszek o wysokiej rozdzielczości (reprezentowanych przez WriteableBitmap) z pojedynczą bitmapSource o niskiej rozdzielczości. Moje duszki są naprawdę ogromne, więc trzeba je generować tylko na raz. Problem polega na renderowaniu sprite'ów do pojedynczego, mniejszego RenderTargetBitmap (Canvas), aby je połączyć; to zasadniczo nie działa poprawnie, począwszy od .NET 4.0.Microsoft powiedział, że wygląda na błąd i przypisano deweloperom zespołów produktu, aby to naprawić. –

Powiązane problemy