Растровая Графика
Необходимость Растровой Графики
До 1980-го года, под Компьютерной Графикой понималось исключительно Векторная Графика. Векторные дисплеи удивляли быстрой прорисовкой линейных графиков, которые были намного элегантней толстых горизонтальных строк телевизоров. В то время считалось абсурдным, что когда-нибудь телевизоры заменят элегантные компьютерные (векторные) изображения. Никто не считал недостатком то, что векторные мониторы не могут залить какие-то области, и что эти мониторы не имеют возможности рисовать разными цветами.
С другой стороны телевизоры были дешевы и более распространены, в отличии от дорогих и редких векторных мониторов. В итоге компьютерная индустрия должна была использовать телевизионную технику, чтобы выйти на массовый рынок. Нужно было связать линейно-адресную цифровую машину (Компьютер) со строчно-разверточной аналоговой машиной (Телевизором).
Так был создан один из базовых элементов компьютера, который сегодня мы называем графическая карта, включающая в себя:
1) быстрая RAM для записи растровой матрицы (= видео-память)
2) быстрая адресация матрицы синхронно с аналоговыми сигналами вертикальной и горизонтальной развертки (=видео-контроллер).
3) быстрый цифровой-аналоговый преобразователь, который преобразовывает данные матрицы в аналоговый видео сигнал ( =ЦАП)
Таким образом полученная растровая графика выглядела ужасно - области c лестничными краями и изображения реального мира, собранные из мерцающих блоков. Слияние компьютера и телевизора было поначалу неудачным. Быстро выяснилось, что достаточно качественная растровая графика требует в два раза большего разрешения и скорости, чем может предложить телевизор. Из этого тупика вышли два новых продукта - компьютерный монитор и графическая карта.
Заливка площадей, многоцветность, повышение разрешения и частоты развертки обеспечили прорыв растровой графике, не смотря на то, что она все еще не позволяла отображать тонкие линии и кривые (это было возможно только через лестничную симуляцию). Растровая графика тянет за собой огромную редундантность и тяжела в программировании. По этой причине почти за каждой растровой графикой стоит векторная графика (исключения - фото и видео).
Растровая Матрица
Растровая матрица – это прямоугольная организация данных (типа Byte, UInt16, UInt32 или Color) по ширине в виде колонок и по высоте в виде строк.
Индекс колонок - column index: x, где 0 <= x < width.
Индекс строк - row index: y, где 0 <= y < height.
Пример:
Белый человечек (Homunculus) на черном фоне
Количество колонок = width = 9
Количество строк = height = 10
0 0 0 9 9 9 0 0 0
0 0 0 9 3 9 0 0 0 [4,1] = Рот
0 0 0 9 9 9 0 0 0
0 0 0 0 9 0 0 0 0 [4,3] = Шея
3 4 5 9 9 9 5 4 3 [0,4] и [8,4] = Руки
0 0 0 9 8 9 0 0 0 [4,5] = Пупок
0 0 0 9 9 9 0 0 0
0 0 0 6 0 6 0 0 0
0 0 0 5 0 5 0 0 0
0 0 0 5 0 5 0 0 0 [3,9] und [5,9] = Ноги
Примеры определения матрицы изображения M с 32-Bit-цветом пикселя = ARGB-Пиксел:
C++ как Array : int M[height][width];
Java как Array : int[][] M = new int[height][width];
C# как Array : Color[,] M = new Color[height, width];
C# как Bitmap: Bitmap M = new Bitmap( width, height, PixelFormat.Format32bppArgb );
На первый взгляд странно, но во всех языковых вариантах, при определении Растровых-Массивов, требуется указание y-координат до х-координат. Причина – Матрицы разворачиваются в памяти компьютера, как одномерные линейные массивы. Интуитивно понятная последовательность - 0-я строка, 1-я строка и т.д. до (height-1)-й строки – возможна только если первый индекс – y.
Следствие: если мы хотим сделать рот Человечка (Homunculus) черным, то должны написать следующий код:
C++ как Array : Homunculus[1][4] = 0;
Java как Array : Homunculus[1][4] = 0;
C# как Array : Homunculus[1,4] = Color.Black;
C# как Bitmap: Homunculus.SetPixel( 4, 1, Color.Black );
Линейная адресация
Растровая Матрица – это лишь словесная конструкция, так как компьютер понимает лишь линейное адресное пространство и записывает все матрицы линейно. В качестве аналогии [y][x]-Матрицу лучше всего представить в виде комода с выдвижными ящиками. В памяти компьютера все ящики вынуты и разложены друг за другом. Сначала идет Строка 0, затем Строка 1 и т.д. до строки height-1. При обращении к пикселу M[y][x], на самом деле высчитывается линейный адрес M + y * width + x. Это значит, что за удобный доступ в виде M[y][x] приходится платить двумя дополнительными сложениями и одним умножением.
Следовательно: при миллионах пикселей матричная адресация очень медлена.
Лучше: использование указателей для быстрых операций на изображениях.
Пример: медленный код обнуления матрицы M[height][width]:
for ( y=0; y < height; y++ )
for ( x=0; x < width; x++ )
M[y][x] = 0;
Пример: быстрый код для обнуления матрицы M[height][width]:
int* pointer = M;
for ( i=0; i < width*height; i++ ) *pointer++ = 0;
Pixel
Pixel - это сокращение от "Picture Element" обозначающее минимальный не квантуемый элемент Растровой Матрицы. Матрица всегда содержит Пикселы только одного типа, при этом существует множество типов Пикселей или их форматов.
Примеры:
1bppIndexed | 1 bit per pixel with indexed color. Requires a LUТ with 2 colors in it. For binary images. |
4bppIndexed | 4 bits per pixel, indexed. Requires a LUT with 3x16 palette entries. |
8bppIndexed | 8 bits per pixel, indexed. Requires a LUT with 3x256 palette entries. |
16bppGrayScale | 16 bits per pixel. The color information specifies 65536 shades of gray. |
16bppRgb555 | 16 bits per pixel; 5 bits each are used for the red, green, and blue components. The remaining bit is not used. |
24bppRgb | 24 bits per pixel; 8 bits each are used for the red, green, and blue components. |
32bppArgb | 32 bits per pixel; 8 bits each are used for the alpha, red, green, and blue components. |
32bppRgb | 32 bits per pixel; 8 bits each are used for the red, green, and blue components. The remaining 8 bits are not used. |
64bppArgb | 64 bits per pixel; 16 bits each are used for the alpha, red, green, and blue components. |
False Color Picture = определение для первых 3-х форматов 1bppIndexed, 4bppIndexed und 8bppIndexed.
Gray Scale Picture = 16bppGrayScale.
True Color Picture = определение для всех других форматов.
Наиболее часто используемые форматы:
1bppIndexed → для бинарных картинок и черно-белых принтеров - для максимальной экономии объема данных.
32bppRgb → для цветных фотографий так как 32-х битная архитектура и 32-х битная адресация отлично подходит для компьютеров.
![]() | Битовая раскладка одного пиксела: 32bppRgb, 24bppRgb und 16bppRgb555 |
Сравнение Векторная Графика ↔ Растровая Графика
Общее сравнение
Векторная Графика | Растровая Графика | |
Основная структура данных | Полигон: PointF[] p = new PointF[n]; | Матрица: Bitmap bmp = new Bitmap( width, height, PixelFormat.Format32bppArgb ); |
Прочие Структуры | Прямоугольник, Эллипс, Сплайн | RLC, Crack Code, MPEG |
Форматы файлов | WMF, PostScript, XAML, PDF, Flash | BMP, GIF, JPEG, MPEG, PNG, TIFF, AVI |
Объем памяти | низкий: n*sizeof(PointF) | огромный: width*height*sizeof(Color) |
Тонкие линии | отлично | Только вертикальные и горизонтальные линии |
Заливка площадей | только штриховкой | хорошо, но с грубыми краями |
Шрифты | слишком тонкие, но хорошо масштабируемые = один шрифт для всех размеров = TrueType | хорошо, но с грубыми краями, для каждого размера нужен свой шрифт. |
Реальные картинки | ноль, только контуры | хорошо, TV |
Многоцветность | почти всегда одноцветная, максимум 2 цвета возможны. | хорошо: почти всегда RGB |
Источник создания | почти всегда - Человек | почти всегда Машина: a) цифровые фото реального мира (Фото-Видео-Камера) b) рендер из Векторной Графики (Графическая карта) |
Математика | Работают все законы аналитической геометрии. | работает только новая Цифровая Геометрия |
Мерцание | мерцает только если у Полигона много углов | мерцает независимо от содержания картинки |
Предназначение | Контурное Рисование : CAD, Комиксы | Картинки с цветными или серыми площадями |
flüchtige Ausgabe | CRT = Векторные мониторы | Строчные-CRT, Flat Panel Display = Растровые мониторы |
dauerhafte Ausgabe | Плоттер | Принтер |
Сравнение операций scroll, zoom, rotate
scroll, zoom, rotate Полигона p0 → p1 | scroll, zoom, rotate Битмапа bmp0 → bmp1 |
все x,y - действительные числа | все x,y - целые числа |
гладкость | ступенчатость, пикселизация |
всегда высокая точность | почти всегда ошибка округления |
прямая трансформация из p0 в p1: трансформируем каждый Вертекс из p0 в p1 | Обратная трансформация из bmp1 в bmp0: ищем для каждого Пиксела из bmp1 один Пиксел из bmp0 |
нет границ | большая проблема: потери на границах картинки |
полная реверсивность | Операции почти никогда нельзя сделать обратными |
операции можно делать последовательно | Операции всегда должны проводится с оригиналом bmp0! |
p0 можно перезаписать значениями p1 | bmp0 всегда нужен в оригинале, нельзя перезаписывать значениями из bmp1! |
Сравнение кода операции Scroll (Сдвиг на: float dx, float dy)
Vektor-Scroll Полигона p0 → p1 | Raster-Scroll Битмапа bmp0 → bmp1 |
for all 0 <= i < n { p1[i].x = p0[i].x + dx; p1[i].y = p0[i].y + dy; } | int idx = Convert.ToInt32( dx ); //rounding int idy = Convert.ToInt32( dy ); //rounding for ( int y1=0; y1 < bmp1.Height; y1++ ) { int y0 = y1 - idy; //backward if ( y0 < 0 || y0 >= bmp0.Height ) continue; //outside for ( int x1=0; x1 < bmp1.Width; x1++ ) { int x0 = x1 - idx; //backward if ( x0 < 0 || x0 >= bmp0.Width ) continue; //outside Color color = bmp0.GetPixel( x0, y0 ); bmp1.SetPixel( x1, y1, color ); } } |
Сравнение кода операции Zoom (Масштабирование: float zoomx, float zoomy)
Vektor-Zoom Полигона p0 → p1 | Raster-Zoom Битмапа bmp0 → bmp1 |
//Центр растяжения в точке(0/0) for all 0 <= i < n { p1[i].x = p0[i].x * zoomx; p1[i].y = p0[i].y * zoomy; } | for ( int y1=0; y1 < bmp1.Height; y1++ ) { int y0 = Convert.ToInt32( y1 / zoomy ); //backward if ( y0 < 0 || y0 >= bmp0.Height ) continue; //outside for ( int x1=0; x1 < bmp1.Width; x1++ ) { int x0 = Convert.ToInt32( x1 / zoomx ); //backward if ( x0 < 0 || x0 >= bmp0.Width ) continue; //outside Color color = bmp0.GetPixel( x0, y0 ); bmp1.SetPixel( x1, y1, color ); } } |
Сравнение кода операции Rotation (Поворот на α Grad по часовой стрелке)
Vektor-Rotation на угол α Полигона p0 → p1 | Raster-Rotation на угол α Битмапа bmp0 → bmp1 |
//Центр вращения в точке (0/0) double arcus = alpha * 2 * Math.PI / 360; float sinus = (float)Math.Sin( arcus ); float cosinus = (float)Math.Cos( arcus ); for all Angles 0 <= i < n { p1[i].x = p0[i].x * cosinus - p0[i].y * sinus; p1[i].y = p0[i].x * sinus + p0[i].y * cosinus; } | double arcus = alpha * 2 * Math.PI / 360; float sinus = (float)Math.Sin( arcus ); float cosinus = (float)Math.Cos( arcus ); for ( int y1=0; y1 < bmp1.Height; y1++ ) { float y1_sinus = y1 * sinus; float y1_cosinus = y1 * cosinus; for ( int x1=0; x1 < bmp1.Width; x1++ ) { int x0 = Convert.ToInt32( x1 * cosinus + y1_sinus ); if ( x0 < 0 || x0 >= bmp0.Width ) continue; int y0 = Convert.ToInt32( -x1 * sinus + y1_cosinus ); if ( y0 < 0 || y0 >= bmp0.Height ) continue; Color color = bmp0.GetPixel( x0, y0 ); bmp1.SetPixel( x1, y1, color ); } } |
| Внимание: Результат выше приведенной операции вращения плохой, т.к. округления Улучшение: Можно дополнить операцию вращения Билинейным фильтром, который собирает целевой пиксел из 4-х оригинальных, накрытых целевым, с учетом процентного отношения соответствующих площадей. Но и этот способ не лишен артефактов, как видно на картинке 4). Дополнительные замечания к операции Raster-Rotation: |