Использование метода Graphics.FillRectangle на 8-битном изображении - PullRequest
0 голосов
/ 11 декабря 2018

Я пытаюсь добавить черный баннер вверху и внизу изображения.Я могу добавить баннер, но формат пикселя полученного растрового изображения изменился на 32-битный.Есть ли способ получить 8-битное растровое изображение в результате.Как уже упоминалось здесь , если я установлю 8-битный pixelFormat в конструкторе, создание графики вызовет исключение.Я читал, что если я конвертирую из 32 в 8, возможно, значения пикселей будут отличаться от исходных.Не знаю, смогу ли я создать новое растровое изображение с нужной высотой и добавить пиксели черных баннеров, используя циклы for.У кого-нибудь есть лучший и простой способ?

Мой код такой, как показано ниже:

            Bitmap img = image as Bitmap;

            using (Bitmap bitmap = new Bitmap(img.Width, img.Height + 200*2)) // create blank bitmap of desired size
            using (Graphics graphics = Graphics.FromImage(bitmap))
            {
                SolidBrush brush = new SolidBrush(Color.White);
                graphics.FillRectangle(brush, 0, 0, img.Width,200);
                // draw existing image onto new blank bitmap
                graphics.DrawImage(img, 0, 200, img.Width, img.Height);

                // draw your rectangle below the original image
                graphics.FillRectangle(brush, 0, 200 + img.Height, img.Width, 200);
               // bitmap.Save(@"c:\Test1.bmp");
            }

1 Ответ

0 голосов
/ 11 декабря 2018

Вот процедура, которая выполняет FillRectangle для bmp8bpp растрового изображения.

Вы передаете индекс, который, если положительный, будет использоваться для помещения цвета в палитру.Если вы передадите отрицательный индекс, он попытается найти цвет, а если он не найдет его, он поместит цвет в конец индекса.Это заменит любой цвет, который был раньше!

void FillIndexedRectangle(Bitmap bmp8bpp, Rectangle rect, Color col, int index)
{
    var pal = bmp8bpp.Palette;
    int idx = -1;
    if (index >= 0) idx = index;
    else
    {
        if (pal.Entries.Where(x => x.ToArgb() == col.ToArgb()).Any())
            idx = pal.Entries.ToList().FindIndex(x => x.ToArgb() == col.ToArgb());
        if (idx < 0) idx = pal.Entries.Length - 1;
    }

    pal.Entries[idx] = col;
    bmp8bpp.Palette = pal;

    var bitmapData = 
        bmp8bpp.LockBits(new Rectangle(Point.Empty, bmp8bpp.Size),
                         ImageLockMode.ReadWrite, bmp8bpp.PixelFormat);
    byte[] buffer=new byte[bmp8bpp.Width*bmp8bpp.Height];

    Marshal.Copy(bitmapData.Scan0, buffer,0, buffer.Length);

    for (int y = rect.Y; y < rect.Bottom; y++)
    for (int x = rect.X; x < rect.Right; x++)
    {
            buffer[x + y * bmp8bpp.Width] = (byte)idx;
    }

    Marshal.Copy(buffer, 0, bitmapData.Scan0,buffer.Length);
    bmp8bpp.UnlockBits(bitmapData);
}

Примеры вызовов:

Bitmap img = new Bitmap(200, 200, PixelFormat.Format8bppIndexed);
FillIndexedRectangle(img, new Rectangle(0,0,200, 200), Color.Silver, 21);
FillIndexedRectangle(img, new Rectangle(23, 23, 55, 99), Color.Red, 22);
FillIndexedRectangle(img, new Rectangle(123, 123, 55, 33), Color.Black, 23);
FillIndexedRectangle(img, new Rectangle(1, 1, 123, 22), Color.Orange, 34);
FillIndexedRectangle(img, new Rectangle(27, 27, 16, 12), Color.Black, -1);
img.Save("D:\\__bmp8bpp.png");

Результат:

__bmp8bpp

Есть возможности для улучшения:

  • Отсутствует вся проверка ошибок в битах блокировки, как по пиксельному формату, так и по данным прямоугольника
  • Вместо этого было бы неплохо добавить цвета с более динамичной схемойдобавление в конец
  • Схема для нахождения ближайшего цвета, уже находящегося в палитре, также может подойти
  • Схема для рисования с прозрачностью также подойдет.Для этого все необходимые новые цвета должны быть определены в первую очередь;также режим прозрачности.
  • Может быть, следует вернуть index, использованный в методе, чтобы следующие вызовы могли ссылаться на него ..

Для других форм, кроме прямоугольников, можно использоватьподпрограмма копирования, которая сначала рисует их на «нормальном» растровом изображении 32bpp, а затем передает пиксели в буфер.

Обновление: Вот несколько строк для добавления (**) илиизменить (*), чтобы разрешить рисование незаполненных прямоугольников;stroke 0 заполняет прямоугольник ..:

 void FillIndexedRectangle(Bitmap bmp8bpp, Rectangle rect, 
                           Color col, int index, int stroke)  // * 

...
...
Marshal.Copy(bitmapData.Scan0, buffer,0, buffer.Length);
Rectangle ri = rect;                               //**
if (stroke > 0) ri.Inflate(-stroke, -stroke);      //**
for (int y = rect.Y; y < rect.Bottom; y++)
for (int x = rect.X; x < rect.Right; x++)
{
        if (ri == rect || !ri.Contains(x,y))      //**
            buffer[x + y * bmp8bpp.Width] = (byte)idx;
}

Marshal.Copy(buffer, 0, bitmapData.Scan0,buffer.Length);
...