Используя System.Drawing, как я могу нарисовать что-то, что имитирует эффект желтого маркера выделения? - PullRequest
6 голосов
/ 13 февраля 2010

Я хочу, чтобы то, что "позади" желтого цвета, показывалось насквозь.

РЕДАКТИРОВАТЬ 1: Но если я рисую «белый», я бы хотел, чтобы цвет маркера сохранил свою чистую желтизну.

РЕДАКТИРОВАТЬ 2: @ Ответ Кевина, вероятно, правильный, и я пометил его правильно, хотя я и не кодировал его. В моем коде я согласен на ответ @ Guffa, используя Color.FromArgb.

РЕДАКТИРОВАТЬ 3: я опубликовал код, который работает с достойной производительностью. Да, вычитание синего является основной идеей, но вы не можете сделать это с помощью API высокого уровня, а SetPixel работает слишком медленно. Решение с хорошей производительностью использует Bitmap.LockBits, UnlockBits.

Ответы [ 5 ]

9 голосов
/ 13 февраля 2010

Маркер - это пигмент, поэтому он по сути вычитает - вы хотите превратить белый в желтый, а не черный в желтый. Я не знаю .NET, но вам нужен нестандартный «режим смешивания», а именно вычитание . В частности, установите режим смешивания, чтобы вычитать, и ваш цвет чистый синий (цвет, который вы хотите вычесть, оставляя желтый). Черные останутся в одиночестве, поскольку синего цвета не нужно вычитать, а белый станет желтым.

К сожалению, многие современные интерфейсы рисования не используют режимы наложения, кроме альфа, и похоже, что это может быть один из них. Если у вас есть доступ к растровому изображению, вы можете реализовать его самостоятельно - взять значение каждого пикселя и установить синий компонент на ноль. Или, если область, которую вы хотите выделить, сложная, то: сделайте копию своего изображения, нарисуйте черный цвет над выделенной областью в копии, затем объедините красный и зеленый каналы из оригинала и синий канал из копии в финал изображение результата.

3 голосов
/ 13 февраля 2010

Вы используете полупрозрачный цвет, например с непрозрачностью 50% альфа:

Color.FromArgb(128, Color.Yellow)

При более низком альфа-значении больше фона просвечивает.

2 голосов
/ 14 февраля 2010

Это код, который вам нужен:

    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 );
        }
    }
1 голос
/ 19 декабря 2010

Этот код работает. Это стирает синий компонент каждого RGB, где я хочу желтый. Сначала я попытался использовать Bitmap.GetPixel / SetPixel, но это было мучительно медленно. Использование Lock / Unlock для получения необработанных битов было выполнено достаточно быстро.

                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);
0 голосов
/ 13 февраля 2010

Попробуйте использовать SolidBrush, инициализированный значением Color с альфа-значением менее 255. Это должно создать полупрозрачную кисть того цвета, который вы используете.

...