2010-02-13 22 views
6

Quiero que se vea lo que está "detrás" del amarillo.Usando System.Drawing, ¿cómo puedo dibujar algo que imite el efecto de un marcador de resaltado amarillo?

EDITAR 1: Pero, si estoy dibujando en "blanco", me gustaría que el color del marcador conserve su amarillez pura.

EDIT 2: la respuesta de @ Kevin es probablemente correcta, y la marqué correctamente, aunque no la codifiqué. En mi código, me estoy conformando con la respuesta de @ Guffa, usando Color.FromArgb.

EDIT 3: He publicado un código que funciona con un rendimiento decente. Sí, restar el azul es la idea básica, pero no puedes hacerlo con un API de alto nivel, y SetPixel es demasiado lento. La solución con buen rendimiento utiliza Bitmap.LockBits, UnlockBits.

Respuesta

1

Este código funciona. Borra el componente azul de cada RGB donde quiero amarillo. Al principio intenté usar Bitmap.GetPixel/SetPixel, pero eso fue muy lento. Usar Bloquear/Desbloquear para obtener los bits crudos se realizó lo suficientemente rápido.

   using (Bitmap tempBitmap = new Bitmap(bitmap.Width, bitmap.Height)) 
       { 
        using (Graphics tempG = Graphics.FromImage(tempBitmap)) 
        { 

         tempG.DrawLines(penYellowHighlighter, stroke.points.ToArray()); 

         // get the raw bits of the source and target and remove the blue from every 
         // bit of the target where there is a yellow bit of the source 
         Rectangle rect = new Rectangle(0, 0, bitmapWithStrokes.Width, bitmapWithStrokes.Height); 

         // lock 
         System.Drawing.Imaging.BitmapData sourceData = 
          tempBitmap.LockBits(
           rect, 
           System.Drawing.Imaging.ImageLockMode.ReadOnly, 
           tempBitmap.PixelFormat); 

         System.Drawing.Imaging.BitmapData targetData = 
          bitmapWithStrokes.LockBits(
           rect, 
           System.Drawing.Imaging.ImageLockMode.ReadWrite, 
           bitmapWithStrokes.PixelFormat); 

         // Get the address of the first line. 
         IntPtr sourcePtr = sourceData.Scan0; 
         IntPtr targetPtr = targetData.Scan0; 

         // Declare an array to hold the bytes of the bitmap. 
         int numberOfBytes = Math.Abs(sourceData.Stride) * tempBitmap.Height; 

         byte[] sourceRgbValues = new byte[numberOfBytes]; 
         byte[] targetRgbValues = new byte[numberOfBytes]; 

         // Copy the RGB values into the array. 
         System.Runtime.InteropServices.Marshal.Copy(sourcePtr, sourceRgbValues, 0, numberOfBytes); 
         System.Runtime.InteropServices.Marshal.Copy(targetPtr, targetRgbValues, 0, numberOfBytes); 

         for (int p = 0; p < numberOfBytes; p += 4) 
         { 
          // if the source's red is yellows's red 
          if (sourceRgbValues[p + 2] == yellowsRedComponent) 
          { 
           // wipe out the target's blue 
           targetRgbValues[p] = 0; 
          } 
         } 

         // Copy the RGB values back to the bitmap 
         System.Runtime.InteropServices.Marshal.Copy(targetRgbValues, 0, targetPtr, numberOfBytes); 

         // Unlock the bits. 
         tempBitmap.UnlockBits(sourceData); 
         bitmapWithStrokes.UnlockBits(targetData); 
3

se utiliza un color de semi transparente, por ejemplo con una opacidad alfa 50%:

Color.FromArgb(128, Color.Yellow) 

Con un valor alfa inferior más de los antecedentes se mostrará a través.

+1

Gracias - Esto funciona, pero no del todo como esperaba. Quiero que el amarillo sea más ... amarillo. –

+0

@Corey: Sí, no es exactamente el efecto de un marcador amarillo, pero es lo más cercano que se puede obtener con las capacidades de dibujo integradas. Aumentar el valor alfa hará que el amarillo sea más claro, pero hará que brille menos el fondo. – Guffa

0

Intente utilizar un SolidBrush inicializado con un valor de Color con un alfa de menos de 255. Esto debería crear un pincel semitransparente del color que utiliza.

9

Un resaltador es un pigmento, por lo que es esencialmente sustractivo: desea convertir el blanco en amarillo, pero no negro en amarillo. No sé .NET, pero lo que desea es un "modo de fusión" no predeterminado, específicamente reste. Específicamente, establezca el modo de fusión para restar y su color a azul puro (el color que desea restar, dejando amarillo). El negro se dejará solo, ya que no hay azul para restar, y el blanco se volverá amarillo.

Desafortunadamente, muchas interfaces de dibujo modernas dejan fuera los modos de fusión distintos de alfa, y parece que este podría ser uno de ellos. Si tiene acceso al mapa de bits, puede implementarlo usted mismo: tome cada valor de píxel y establezca el componente azul en cero. O si el área que desea resaltar es complicada, entonces: haga una copia de su imagen, dibuje negro sobre el área resaltada en la copia, luego combine los canales rojo y verde del original y el canal azul de la copia en el final imagen de resultado.

+0

¡Yikes! Esto está más allá de mi nivel de ambición, pero parece que tu respuesta es la correcta si no quiero comprometer el efecto, si quiero que el amarillo sea amarillo. –

+0

Bueno, es muy simple: solo tiene que configurar un parámetro - * si * tiene un sistema de dibujo que admite modos de fusión. –

+0

¿Es esto lo mismo que "quemadura de color" en algunas aplicaciones gráficas? Por ejemplo, paint.net? (¡Aprendiendo algo nuevo todos los días!: D) –

2

Este es el código que necesita:

protected override void OnPaint(PaintEventArgs e) 
    { 
     using (var bmp = new Bitmap(100, 100)) 
     using (var g = Graphics.FromImage(bmp)) 
     using (var ia = new ImageAttributes()) 
     { 
      // 1. create a sample bitmap 
      g.Clear(Color.White); 
      var p = Point.Empty; 
      foreach (var color in new Color[] { Color.Black, Color.Gray, Color.LightBlue, Color.Green, Color.Red, Color.Magenta }) 
       using (var brush = new SolidBrush(color)) 
       { 
        g.DrawString("Some sample text", SystemFonts.DefaultFont, brush, p); 
        p.Offset(0, 16); 
       } 
      // 2. transfer the bitmap on screen 
      e.Graphics.DrawImage(bmp, Point.Empty); 
      // 3. transfer a part of the bitmap on screen again, this time removing all blue 
      ia.SetColorMatrix(new ColorMatrix(new float[][] { 
         new float[] {1, 0, 0, 0, 0}, 
         new float[] {0, 1, 0, 0, 0}, 
         new float[] {0, 0, 0, 0, 0}, 
         new float[] {0, 0, 0, 1, 0}, 
         new float[] {0, 0, 0, 0, 1}})); 
      e.Graphics.DrawImage(
       bmp, 
       new Rectangle(30, 0, 40, 100), 
       30, 0, 40, 100, 
       GraphicsUnit.Pixel, 
       ia); 
     } 
    } 
+0

Aunque el código anterior demuestra el principio de restar el azul, lo que falta en esta respuesta es cómo adaptar esta idea a golpes irregulares. Entre las respuestas publiqué un código que muestra cómo hacer que los trazos irregulares se vean como un marcador. –

+0

@ Corey Trager: use la transformación para dibujar un fondo amarillento y luego use eso como un pincel para dibujar sus formas. – supercat

Cuestiones relacionadas