2010-02-13 4 views
6

Je veux montrer ce qui est derrière le jaune.En utilisant System.Drawing, comment puis-je dessiner quelque chose qui imite l'effet d'un marqueur de surbrillance jaune?

EDIT 1: Mais si je dessine sur "blanc", j'aimerais que la couleur du marqueur conserve son jaune pur.

EDIT 2: @ La réponse de Kevin est probablement correcte, et je l'ai marqué correctement, même si je ne l'ai pas codé. Dans mon code, je me contenterai de la réponse de @ Guffa, en utilisant Color.FromArgb.

EDIT 3: J'ai posté du code qui fonctionne avec des performances décentes. Oui, soustraire le bleu est l'idée de base, mais vous ne pouvez pas le faire avec une API de haut niveau, et SetPixel est trop lent. La solution avec de bonnes performances utilise Bitmap.LockBits, UnlockBits.

Répondre

1

Ce code fonctionne. Il efface le composant bleu de chaque RGB où je veux le jaune. Au début, j'ai essayé d'utiliser Bitmap.GetPixel/SetPixel, mais c'était douloureusement lent. L'utilisation de Lock/Unlock pour obtenir les bits bruts a été effectuée assez rapidement.

   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

Vous utilisez une couleur semi-transparente, par exemple avec une opacité alpha 50%:

Color.FromArgb(128, Color.Yellow) 

Avec une valeur alpha inférieure plus de l'arrière-plan se montrer à travers.

+1

Merci - Cela fonctionne, mais pas tout à fait comme je l'avais espéré. Je veux que le jaune lui-même soit plus ... jaune. –

+0

@Corey: Oui, ce n'est pas exactement l'effet d'un marqueur jaune, mais il est aussi proche que possible des capacités de dessin intégrées. Si vous augmentez la valeur alpha, le jaune sera plus net, mais il perdra de son éclat. – Guffa

0

Essayez d'utiliser un SolidBrush initialisé avec une valeur Color avec un alpha inférieur à 255. Cela devrait créer un pinceau semi-transparent de la couleur que vous utilisez.

9

Un surligneur est un pigment, donc il est essentiellement soustractif - vous voulez transformer le blanc en jaune, mais pas le noir en jaune. Je ne sais pas. NET, mais ce que vous voulez est un "mode de fusion" non par défaut, en particulier soustraire. Spécifiquement, réglez le mode de fusion à soustraire et votre couleur à bleu pur (la couleur que vous voulez soustraire, laissant jaune). Le noir sera laissé seul, puisqu'il n'y a pas de bleu à soustraire, et le blanc deviendra jaune. Malheureusement, de nombreuses interfaces de dessin modernes ignorent les modes de fusion autres que alpha, et il semble que ce soit l'un d'entre eux. Si vous avez accès au bitmap, vous pouvez l'implémenter vous-même - prenez chaque valeur de pixel et mettez le composant bleu à zéro. Ou si la zone que vous voulez surligner est compliquée, faites une copie de votre image, dessinez en noir sur la zone en surbrillance dans la copie, puis combinez les canaux rouge et vert de l'original et le canal bleu de la copie à la finale image du résultat

+0

Yikes! C'est au-delà de mon niveau d'ambition, mais il semble que votre réponse soit la bonne réponse si je ne veux pas compromettre l'effet, si je veux que le jaune soit jaune. –

+0

Eh bien, c'est très simple - juste un paramètre - * si * vous avez un système de dessin qui supporte les modes de fusion. –

+0

Est-ce la même chose que "la couleur" dans certaines applications graphiques? Par exemple, paint.net? (Apprenant quelque chose de nouveau chaque jour!: D) –

2

C'est le code dont vous avez besoin:

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

Bien que le code ci-dessus démontre le principe de la soustraction du bleu, ce qui manque dans cette réponse, c'est comment adapter cette idée à des traits irréguliers. Parmi les réponses j'ai posté du code qui montre comment faire des traits irréguliers ressemblent à un marqueur. –

+0

@Corey Trager: utilisez la transformation pour dessiner un arrière-plan jauni, puis utilisez-le comme un pinceau pour dessiner vos formes. – supercat