C# Merge BitmapImages

holy

Lieutenant
Registriert
Aug. 2008
Beiträge
534
Hallo ihr,

ich habe folgenden Code.
Code:
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Threading.Tasks;

using Windows.Storage;
using Windows.Storage.FileProperties;
using Windows.UI.Xaml.Media.Imaging;

Code:
private static readonly object Locker = new object();
private static volatile BitmapImage _defaultCover;

private BitmapImage _cover;
public BitmapImage CoverArt 
{
  get { return _cover ?? _defaultCover; }
  set { SetProperty( ref _cover, value ); }
}

Code:
private async Task LoadThumbnailAsync( StorageFile file )
{
  if ( file == null )
  {
    return;
  }

  var thumb = await file.GetThumbnailAsync( ThumbnailMode.MusicView, 192, ThumbnailOptions.UseCurrentScale );
  if ( thumb.Type == ThumbnailType.Image )
  {
    var cover = new BitmapImage();
    cover.SetSource( thumb );
    CoverArt = cover;
    return;
  }

  if ( CoverArt == null )
  {
    lock ( Locker )
    {
      if ( CoverArt == null )
      {
        var cover = new BitmapImage();
        cover.SetSource( thumb );
         _defaultCover = cover;
        OnPropertyChanged( "CoverArt" );
      }
    }
  }
}

In Zeile 23 bis 26 wird ein BitmapImage aus einem Icon mit transparenten Hintergrund (thumb) erzeugt. Weil transparent in diesem Fall nicht das ist, was ich möchte, versuche ich nun dieses Bitmap über eine Anderes zu legen. Das Problem ist, ich weiß nicht wie ;) Kann mir jemand helfen?

Danke und Gruß
 
Zuletzt bearbeitet:
Das hilft mir leider nicht weiter. Trotzdem danke.
 
Kommst du denn Pixelweise an die Bilddaten ran?
Du könntest dann einfach
for y for x
new_r = (r1+r2) /2
new_g = (g1+g2) /2
new_b = (b1+b2) /2
machen
 
Per-Pixel funktioniert. Zwar etwas umständlich, aber es geht ;)
Falls es irgendwem irgendwann helfen sollte, hier der Code.
So wie er da steht ist er "inkompatibel" zu dem lock (kann dann jeder für sich lösen ;))
Noch ein Hinweis: das Bild muss eine Windows-Bitmap sein (siehe Offset). Farbtiefe, Komprimierung etc. muss (falls es überhaupt jemand braucht) selber checken. Für mich reicht der Code unten aus.

Code:
if ( CoverArt == null )
{
  var thumbStream = thumb.GetInputStreamAt( thumb.Position );
  DataReader reader = new DataReader( thumbStream );
  await reader.LoadAsync( (uint)thumb.Size );
  byte[] data = new byte[thumb.Size];
  reader.ReadBytes( data );

  int offset = data[10] + data[11] + data[12] + data[13];
  for ( int i = offset; i < data.Length; i += 4 )
  {
    byte a = data[i + 3];
    byte r = data[i + 2];
    byte g = data[i + 1];
    byte b = data[i];

    // do whatever here...
  }
                                           
  InMemoryRandomAccessStream memStream = new InMemoryRandomAccessStream();
  DataWriter writer = new DataWriter( memStream.GetOutputStreamAt( 0 ) );
  writer.WriteBytes( data );
  await writer.StoreAsync();
  BitmapImage image = new BitmapImage();
  image.SetSource( memStream );

  var cover = new BitmapImage();
  cover.SetSource( memStream );
  _defaultCover = cover;
  OnPropertyChanged( "CoverArt" );
}
Ergänzung ()

Es ist irgendwie zum heulen ;(

Ich hole mal kurz etwas weiter aus.
Um ein wenig "warm" zu werden mit der WinRT bzw. generell mit Metro-Apps, schreibe ich grade eine Musik-App. Der Code oben läd zu einem Album das entsprechende Cover, falls es in einem der enthaltenen Songs integriert ist. Falls nicht, wird erstmal ein Standard-Cover gesetzt. Dieses Standard-Cover entspricht dem Code, in dem ich zwei Bilder vereinigen möchte.

Die Vereinigung zweier Bilder funktioniert. Das Problem ist nun, jedes Image-Control, dass dieses Standard-Cover als Quelle verwendet, flackert bis die komplette Musik-Kollektion eingelesen wurde. Ich kann mir das nicht erklären.
Das Standard-Cover wird genau ein einziges mal geladen und steht dann allen Instanzen zur Verfügung.

Durch double check locking stell ich genau das sicher.
Code:
if ( CoverArt == null )
{
  lock ( Locker )
  {
    if ( CoverArt == null )
    {
      ...
    }
  }
}

Um auszuschließen, dass es an der Manipulation liegt, habe ich erstmal ein PNG als Cover geladen anstatt den Pixel-Buffer manuelle zu manipulieren, d.h. im Rumpf des obigen Codes steht das hier:
Code:
Uri defaultCover = new Uri( "ms-appx:///Assets/defaultCover.png" );
var cover = new BitmapImage( defaultCover );
_defaultCover = cover;
OnPropertyChanged( "CoverArt" );

Das Problem wird dadurch allerdings nicht gelöst.

Wie auch immer. Das Problem kann ich lösen (will ich aber nicht) indem ich das Standard-Cover als Member einer Instanz der Klasse implementiere und nicht des Types.
Code:
private static volatile BitmapImage _defaultCover;
wird zu
Code:
private BitmapImage _defaultCover;

Hat irgendwer eine Ahnung was genau da passiert, wenn ich gegen ein static field binde? Dieses "Phänomen" ist mir genauso unerklärlich wie neu.



Hm. noch eine Ergänzung. Grade bemerkt. Das BitmapImage _defaultCover ist immernoch static.
Code:
if ( _defaultCover == null )
{
  Uri defaultTile = new Uri( "ms-appx:///Assets/default.png" );
  var cover = new BitmapImage( defaultTile );
  _defaultCover = cover;
}

Flackert.

Ohne den Check auf null:
Code:
Uri defaultTile = new Uri( "ms-appx:///Assets/default.png" );
var cover = new BitmapImage( defaultTile );
_defaultCover = cover;

Flackert nicht. WTF?
 
Zuletzt bearbeitet:
kuddlmuddl schrieb:
Evtl vollkommen am Thema vorbei aber:
Flackern in GUI applikationen hab ich nur erlebt wenn kein double/tripple buffering verwendet wird
http://de.wikipedia.org/wiki/Doppelpufferung
http://en.wikipedia.org/wiki/Double_buffering#Double_buffering_in_computer_graphics

Ja, das ist so richtig am Thema vorbei :evillol:
Die Frage ist, wieso flackert das Bild (200x200px, nicht der ganze Bildschirm), wenn die Quelle eine Singleton ist bzw. das Singleton auf null geprüft wird?
 
Zuletzt bearbeitet:
Ich weiß jetzt nicht, ob ich es richtig verstanden hab, aber vielleicht liegt es daran, dass das PropertyChangedEvent jedesmal geworfen wird und dadurch sich das Control das Bild neu holen will?

Vielleicht flackert es nicht mehr, wenn du das Event nicht bei jedem SET-Aufruf schmeißt, sondern einmal wenn das Singleton gebaut wird?

Leider hast du nur Bruchstücke deines Codes gepostet, deshalb ist es nicht so einfach, sich das alles zusammenzureihmen ;)
 
Erdmännchen schrieb:
Ich weiß jetzt nicht, ob ich es richtig verstanden hab, aber vielleicht liegt es daran, dass das PropertyChangedEvent jedesmal geworfen wird und dadurch sich das Control das Bild neu holen will?

PropertyChanged wird jedesmal gefeuert wenn sich _cover ändert, aber nur exakt einmal für _defaultCover.

Erdmännchen schrieb:
Vielleicht flackert es nicht mehr, wenn du das Event nicht bei jedem SET-Aufruf schmeißt, sondern einmal wenn das Singleton gebaut wird?

Siehe oben. So mache ich es schon.
 
Zurück
Oben