c# multithreading silverlight windows-phone-7 writeablebitmap

c# - ¿Cómo puedo renderizar texto en un WriteableBitmap en una cadena de fondo, en Windows Phone 7?



multithreading silverlight (6)

En primer lugar, ¿estás seguro de representar esto como un mapa de bits? ¿Qué hay de generar un Canvas con una imagen y TextBlock ?

Necesito renderizar una gran cantidad de imágenes

Tengo la sensación de que esta generación matará el rendimiento del teléfono. En general, para la mainupulación de mapa de bits, la mejor manera es usar XNA. Algunas partes del framework XNA hacen un gran trabajo en los proyectos de Silverlight. (Por cierto, las renovadas herramientas para desarrolladores de Windows Phone permitirán que Silverlight y XNA coexistan en el mismo proyecto)

Me gustaría dar un paso atrás y pensar en esta característica. Desarrollar algo como esto por una semana y luego terminar con un rendimiento inaceptable me haría un panda triste.

EDITAR

Hasta donde entiendo, necesitas algún tipo de ventana emergente con la imagen como fondo y el mensaje.

Crea un lienzo con TextBlock pero ocúltalo.

<Canvas x:Name="userInfoCanvas" Height="200" Width="200" Visibility="Collapsed"> <Image x:Name="backgroundImage"> </Image> <TextBlock x:Name="messageTextBlock" Canvas.ZIndex="3> </TextBlock> <!--ZIndex set the order of elements --> </Canvas>

Cuando reciba el nuevo mensaje, muestre el Lienzo al usuario (una animación de opacidad sería agradable), cuando termine de renderizar en el hilo de fondo.

messageTextBlock.Text = message; backgroundImage.Source = new BitmapImage(renderedImage);

Obviamente, aquí hay un problema con la actualización. Los elementos de la interfaz de usuario se pueden actualizar solo desde el subproceso de interfaz de usuario, por lo tanto, la actualización debe estar en cola con el distribuidor.

Dispatcher.BeginInvoke(DispatcherPriority.Background, messageUpdate); //messageUpdate is an Action or anthing that can be infered to Delegate

PD. no compiló, esto es más pseudocódigo.

Estoy tratando de renderizar texto en un mapa de bits en una aplicación de Windows Phone 7.

El código que se parece más o menos al siguiente funcionaría bien cuando se ejecuta en el hilo principal:

public ImageSource RenderText(string text, double x, double y) { var canvas = new Canvas(); var textBlock = new TextBlock { Text = text }; canvas.Children.Add(textBloxk); Canvas.SetLeft(textBlock, x); Canvas.SetTop(textBlock, y); var bitmap = new WriteableBitmap(400, 400); bitmap.Render(canvas, null); bitmap.Invalidate(); return bitmap; }

Ahora, dado que tengo que renderizar varias imágenes con cosas más complejas, me gustaría renderizar el mapa de bits en una cadena de fondo para evitar una IU que no responde.

Cuando uso un BackgroundWorker para hacerlo, el constructor de TextBlock arroja una UnauthorizedAccessException acceso UnauthorizedAccessException que alega que este es un acceso de hilos cruzados no válido.

Mi pregunta es: ¿cómo puedo renderizar texto en un mapa de bits sin bloquear la interfaz de usuario?

  • No sugiera que use un servicio web para hacer la representación. Necesito renderizar una gran cantidad de imágenes y el costo del ancho de banda no es aceptable para mis necesidades, y la capacidad de trabajar fuera de línea es un requisito importante.
  • La solución no necesariamente tiene que utilizar WriteableBitmap o UIElements , si hay otra forma de representar el texto.

EDITAR

Otro pensamiento: ¿alguien sabe si debería ser posible ejecutar un bucle de mensaje UI en otro hilo, y luego hacer que ese hilo haga el trabajo? (en lugar de usar un BackgroundWorker )?

EDIT 2

Para considerar alternativas a WriteableBitmap , las características que necesito son:

  • Dibuja una imagen de fondo.
  • Mida el ancho y la altura de una cadena de 1 línea, dada la familia y el tamaño de la fuente (y preferiblemente el estilo). No es necesario envolver palabras.
  • Dibuja una cadena de 1 línea, con la familia de fuentes, el tamaño y el estilo, en una determinada coordenada.
  • La representación de texto debe admitir un fondo transparente. Es decir, debería ver la imagen de fondo entre los personajes.

Estoy de acuerdo con la respuesta de Derek: estás tratando de usar controles de UI sin una IU.

Si desea renderizar un mapa de bits, debe seguir las clases para dibujar texto en mapas de bits.

supongo que Windows Phone 7 tiene .NET Compact Framework.

psudeo-code:

public Bitmap RenderText(string text, double x, double y) { Bitmap bitmap = new Bitmap(400, 400); using (Graphics g = new Graphics(bitmap)) { using (Font font = SystemFonts....) { using (Brush brush = new SolidColorBrush(...)) { g.DrawString(text, font, brush, new Point(x, y)); } } } return bitmap; }


La naturaleza misma de los elementos de la interfaz de usuario requiere interacción con ellos en el hilo de la interfaz de usuario. Incluso si pudieras crearlos en un hilo de fondo, cuando trataras de renderizarlos en el WriteableBitmap obtendrías una excepción similar, e incluso entonces si te permitiera hacer eso, los elementos no tendrían realmente un aspecto visual. representación hasta que se agregaron en el árbol visual. Es posible que necesite utilizar una biblioteca genérica de manipulación de imágenes en lugar de utilizar elementos de la interfaz de usuario.

Tal vez podría describir su escenario en una base más amplia, podríamos tener una mejor solución para usted :)


No estoy seguro de si esto resolverá por completo tus problemas, pero hay 2 herramientas que utilizo en mi lector de cómics (no lo voy a enchufar desvergonzadamente aquí, pero estoy tentado ... una pista si estás buscando es ... es "increíble"). Hay momentos en los que necesito unir un montón de imágenes. Yo uso Rene Schulte (y muchos otros colaboradores) WriteableBitmapExtensions ( http://writeablebitmapex.codeplex.com/ ). Pude descargar el procesamiento / costura de una imagen a un hilo de fondo y luego establecer el WriteableBitmap resultante como el origen de alguna imagen en el hilo de UI.

Otra ventaja en este espacio es .NET Image Tools ( http://imagetools.codeplex.com/ ). Tienen muchas utilidades para guardar / leer varios formatos de imagen. También tienen algunos de los niveles bajos, y me gustaría que hubiera una manera fácil de usar ambos (pero no los hay).

Todo lo anterior funciona en WP7.

Supongo que la principal diferencia es que con estas herramientas no usarás XAML, sino que escribirás directamente en tu imagen (por lo que es posible que tengas que hacer la detección de tamaño de tu texto y cosas por el estilo).


Puedes dibujar en WriteableBitmap en hilo, pero tienes que

  1. crear WriteableBitmap en el hilo principal de la interfaz de usuario
  2. dibujar trabajo en el hilo de fondo
  3. Asignar BitmapSource en el hilo principal de la interfaz de usuario

Este método copia las letras de una imagen prefabricada en lugar de usar TextBlock, se basa en mi respuesta a esta pregunta . La principal limitación es que se requiere una imagen diferente para cada fuente y tamaño necesario. Una fuente de tamaño 20 necesita aproximadamente 150 kb.

Con SpriteFont2, exporte la fuente y el archivo de métrica xml en los tamaños que necesite. El código asume que se denominan "FontName FontSize" .png y "FontName FontSize" .xml los agrega a su proyecto y establece la acción de compilación en el contenido. El código también requiere WriteableBitmapEx .

public static class BitmapFont { private class FontInfo { public FontInfo(WriteableBitmap image, Dictionary<char, Rect> metrics, int size) { this.Image = image; this.Metrics = metrics; this.Size = size; } public WriteableBitmap Image { get; private set; } public Dictionary<char, Rect> Metrics { get; private set; } public int Size { get; private set; } } private static Dictionary<string, List<FontInfo>> fonts = new Dictionary<string, List<FontInfo>>(); public static void RegisterFont(string name,params int[] sizes) { foreach (var size in sizes) { string fontFile = name + " " + size + ".png"; string fontMetricsFile = name + " " + size + ".xml"; BitmapImage image = new BitmapImage(); image.SetSource(App.GetResourceStream(new Uri(fontFile, UriKind.Relative)).Stream); var metrics = XDocument.Load(fontMetricsFile); var dict = (from c in metrics.Root.Elements() let key = (char) ((int) c.Attribute("key")) let rect = new Rect((int) c.Element("x"), (int) c.Element("y"), (int) c.Element("width"), (int) c.Element("height")) select new {Char = key, Metrics = rect}).ToDictionary(x => x.Char, x => x.Metrics); var fontInfo = new FontInfo(new WriteableBitmap(image), dict, size); if(fonts.ContainsKey(name)) fonts[name].Add(fontInfo); else fonts.Add(name, new List<FontInfo> {fontInfo}); } } private static FontInfo GetNearestFont(string fontName,int size) { return fonts[fontName].OrderBy(x => Math.Abs(x.Size - size)).First(); } public static Size MeasureString(string text,string fontName,int size) { var font = GetNearestFont(fontName, size); double scale = (double) size / font.Size; var letters = text.Select(x => font.Metrics[x]).ToArray(); return new Size(letters.Sum(x => x.Width * scale),letters.Max(x => x.Height * scale)); } public static void DrawString(this WriteableBitmap bmp,string text,int x,int y, string fontName,int size,Color color) { var font = GetNearestFont(fontName, size); var letters = text.Select(f => font.Metrics[f]).ToArray(); double scale = (double)size / font.Size; double destX = x; foreach (var letter in letters) { var destRect = new Rect(destX,y,letter.Width * scale,letter.Height * scale); bmp.Blit(destRect, font.Image, letter, color, WriteableBitmapExtensions.BlendMode.Alpha); destX += destRect.Width; } } }

Necesitas llamar a RegisterFont una vez para cargar los archivos y luego llamar a DrawString. Utiliza WriteableBitmapEx.Blit, por lo que si su archivo de fuente tiene texto blanco y un fondo transparente, alfa se maneja correctamente y puede volver a colorearlo. El código escala el texto si dibujas en un tamaño que no cargaste pero los resultados no son buenos, se podría usar un mejor método de interpolación.

Intenté dibujar desde un hilo diferente y esto funcionó en el emulador, aún necesitas crear el WriteableBitmap en el hilo principal. Mi comprensión de su escenario es que desea desplazarse por mosaicos similares a cómo funcionan las aplicaciones de mapeo, si este es el caso reutilice los antiguos WriteableBitmaps en lugar de volver a crearlos. Si no, el código podría cambiarse para trabajar con matrices en su lugar.