Графические устройства



 

Подпрограммы для рисования линий

При оформлении рабочей области экрана часто используются одноцветные горизонтальные и вертикальные линии. Способ их рисования зависит от угла наклона и направления линии. Изображение на экране дискретно по своей природе, поэтому гладкими являются только линии, наклоненные под углом кратным 45 градусам, при их рисовании значения одной или обеих координат изменяются от точки к точке на 1. В остальных случаях приращения координат вычисляются по специальным алгоритмам. От направления линии зависят способы переадресации операндов и изменения окна видеопамяти в тех случаях, когда значения адресов выходят за пределы сегмента.

Перечисленные особенности рисования линий подробно обсуждались в разделе, там же было приведено несколько вариантов подпрограмм, выполняющих соответствующие действия при работе в видеорежимах PPG. В данном разделе описано рисование одноцветных горизонтальных линий в прямом направлении (слева направо). При этом, основное внимание уделяется влиянию размера кода точки на выполняемые действия.

Базовые варианты подпрограмм. В примере 7.5 приведены два варианта подпрограмм рисования линии, различающиеся способом пересылки. Перед их вызовом код цвета точки помещается в регистр ах или еах, а размер линии (количество точек) в сх.

Исходный адрес видеопамяти указывается в регистре di, и устанавливается окно видеопамяти, которому принадлежит этот ад-5сс. Как обычно, регистр es должен содержать код видеосегмента (значение теременной vbuff).

Пример 7.5. Цикл рисования горизонтальной линии в режимах Hi-Color

; Вариант 1, используется команда пересылки
lorline: mov es: [di], ax !! для True Color — mov es[di], eax
add di, bytppnt переадресация операнда
jne @F переход, если не нуль
call NxtWin установка следующего окна
}Q: loop horline управление повторами цикла
ret возврат из подпрограммы
; Вариант 2, используется строковая операция.
lorline: stosw !! для True Color — stosd
or di, di начало нового сегмента ?
jne @F -> нет
call NxtWin установка следующего окна
i@: loop horline управление повторами цикла
ret возврат из подпрограммы

Текст примера 7.5 отличается от текста примера 3.6 незначительными изменениями. В первом варианте при переадресации используется не 1, а значение переменной bytppnt, которое равно 2 или 4.

Для использования подпрограмм примера 7.5 в режимах True Color надо первые команды в обоих вариантах заменить командами, указанными в сомментариях. В этих режимах перед вызовом подпрограмм код цвета точек юмещается в регистр еах, поскольку он занимает 32 разряда.

Давайте уточним, почему приведены два варианта циклов, если они содержат одинаковое количество команд. Команда пересылки удобна в тех случаяx, когда переадресация не может выполняться сразу после записи или чтения кода точки (см. пример 6.5), или когда шаг переадресации не совпадает : размером кода точки, например при рисовании вертикальных линий. Если оказанные условия не существенны, то второй вариант цикла 7.5 предпочтительнее. После компиляции он окажется короче первого на 3 байта, и будет выполняться несколько быстрее. Но главное, при определенных условиях юзможно применение микропрограммного цикла, существенно ускоряющего процесс рисования. Об этом мы поговорим особо.

Ускорение цикла рисования

В примере 7.5 после каждой переадресации вы-юлняется проверка принадлежности нового значения адреса текущему сегменту и, в случае необходимости, устанавливается следующее окно видео-тамяти. Вероятность того, что новое значение адреса выйдет за границу :егмента, достаточно мала. Например, при установке видеорежимов 110h или 111h (Hi-color, 640x480) рабочее пространство видеопамяти занимает 9 неполных окон. Если создаваемое изображение заполняет все это пространство, то только в восьми случаях из 307 200 возникнет необходимость изменения окна, а в остальных случаях проверка адресов не требуется. Сказанное не означает, что она вообще не нужна. Для сокращения количества дополнительных действий надо изменить способ проверки.

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

Замечание
Рисование линии по частям в режимах PPG уже описано в разделе, а соответствующая подпрограмма при: ведена в примере 3.8.

Подпрограмма Twopart

Нас интересует не простое рисование, а возможность ускорения выполнения различных действий с точками, расположенными на прямой линии. Поэтому текст примера 7.6 является своеобразным управляющим алгоритмом, который для выполнения конкретных действий вызывает вспомогательную подпрограмм у baseip. В данном примере она рисует прямую линию.

Входные параметры подпрограммы Twopart полностью соответствуют параметрам подпрограммы horiine (пример 7.5) и расположены в тех же регистрах.

Пример 7.6. Рисование линии по част-ям в режимах Hi-Color

push dx coxp аняем содержимое регистра dx
mov dx, di копируем адрес в регистр dx
shl ex, 01 ! ! для True Color - shl ex, 02
add dx, ex сумма исходного адреса и размера линии
jc @F -> прямая расположена в двух окнах
xor dx, dx очис тка регистра dx
@@: sub ex, dx количество точек в текущем окне
shr сх, 01 ! ! для True Color - shr ex, 02
call baseip рисуем всю линию или ее первую часть
or di, di адрес в пределах текущего окна ?
jne hrl_exit -> да, линия нарисована полностью
call NxtWin установка следующего окна
add ex, dx количество не нарисованных точек
je hrl_exit линия нарисована полностью
shr ex, 01 !! для True Color - shr ex, 02
call baselp рисуем остаток линии
hrl exit: pop dx восстановление содержимого dx
ret возврат из подпрограммы
; Подпрограмма, выполняющая основные действия
baselp: mov es: [di],ax !! для True Color — mov es: [di], eax .
add di, bytppnt переадресация операнда
loop baselp управление повторами цикла
ret возврат из подпрограммы

Выполнение примера 7.6 начинается с сохранения в стеке содержимого регистра dx, поскольку оно изменяется в подпрограмме. При входе регистр сх содержит количество рисуемых точек, его надо преобразовать в количество байтов, с помощью сдвига и сложить с исходным адресом видеопамяти (в регистре dx). Если при этом происходит переполнение, то линия не помешается в текущем окне и ее надо рисовать по частям. В противном случае регистр dx очищается. После этого вычисляется количество точек в первой части и вызывается подпрограмма baselp, рисующая начало линии.

При первом вызове baselp может быть нарисована вся линия или только ее первая часть. Это важно знать для выполнения дальнейших действий. Они начинаются с проверки значения адреса, находящегося в регистре di.

Возможен случай, когда при первом рисовании достигнута граница окна. В таком случае в регистре di находится 0, но нулевой адрес принадлежит не текущему, а следующему окну видеопамяти. Поэтому если регистр di очищен, то обязательно надо сменить окно видеопамяти. Если же содержимое di отлично от нуля, то нарисована вся линия.

После установки окна суммируется содержимое регистров сх и dx. Если сумма равна нулю, то линия нарисована целиком, а код ее последней точки был записан в последнее слово сегмента. В противном случае количество байтов, полученное в регистре сх, преобразуется в количество точек и повторно вызывается подпрограмма baselp. Перед возвратом на вызывающий модуль восстанавливается исходное содержимое регистра dx, соответствующая команда имеет метку hri_exit.

Замечание
Практическая ценность примера 7.6 заключается в том, что он иллюстрирует способ обработки строки графического объекта по частям. Основные действия локализованы в подпрограмме baselp. Ее можно изменить так, чтобы вместо записи в видеопамять содержимого регистра ах выполнялись другие действия, например инверсия кодов точек, пересылка кодов из видеопамяти в оперативную или наоборот и т. д.

Ускоренное рисование линии. В подпрограмме baselp, текст которой приведен в примере 7.6, работу с видеопамятью выполняет одна команда, поэтому сразу после нее возможна переадресация операнда. Если при этом шаг переадресации совпадает с размером операнда, то вместо команды пересылки можно использовать строковую операцию. В таком случае тело цикла пересылки сокращается до одной команды rep stosw, которую надо вставить вместо call baselp. Это и сделано в примере 7.7.

Пример 7.7. Ускоренное рисование линии в режимах Hi-Color I

horline: push dx сохраняем содержимое регистра dx
mov dx, di копируем адрес в регистр dx
shl ex, 01 ! ! для True Color - shl ex, 02
add dx, ex сумма исходного адреса и размера линии
jc hrl 1 -> прямая расположена в двух окнах
xor dx, dx очистка регистра dx
hrl_l : sub ex, dx количество точек в текущем окне
shr ex, 01 ! ! для True Color — shr ex, 02
rep stosw ! ! для True Color — stosd
or di, di адрес в пределах текущего окна ?
jne hrl exit -> да, линия нарисована полностью
call NxtWin установка следующего окна
mov ex, dx количество не нарисованных точек
shr ex, 01 ! ! для True Color - shr ex, 02
rep stosw ! ! для True Color — stosd
hrl exit: pop dx восстановление содержимого dx
ret возврат из подпрограммы

Обратите внимание на то, что во второй части примера 7.7 проверяется только содержимое регистра di и не проверяется оставшееся количество точек. Это допустимо потому, что если регистр сх очищен, то цикл rep stosw не будет выполняться и предварительная проверка содержимого сх не обязательна.

В комментариях к примерам 7.6 и 7.7 указано, как надо изменить переменные команды для использования подпрограмм в режимах True Color, в таком случае код цвета линии помещается в регистр еах.

Условное ассемблирование

Тексты трех приведенных примеров рисования линий являются стандартными заготовками, у которых от видеорежима зависит лишь несколько команд. Исключить эту зависимость и сделать подпрограммы универсальными невозможно. Однако их исходный текст можно подготовить так, чтобы Макроассемблер выбирал нужный вариант команды в зависимости от заданного вами признака. Это упростит вашу работу и позволит включать в текст задачи заранее подготовленные и отлаженные исходные тексты подпрограмм.

Признак (условие для выбора) описывается как обычная константа и располагается в начале текста программы перед описанием первого сегмента.

Предположим, что ему присвоено имя variant, а значения равны 1 для режимов Hi-color и 2 для режимов True color. Описание выглядит так:

variant = 1 ; Описание признака "variant" для режимов Hi-Color

В текст подпрограммы, приведенной в примере 7.8, вместо переменной команды вставлен условный блок, в котором описаны варианты выбора.

Пример 7.8. Выбор варианта команды по заданному признаку

IF variant EQ 1 ; проверка условия выбора
mov es:[di], ax ; основной вариант команды
ELSE ; признак альтернативного варианта
mov es:[di], eax ; альтернативный вариант команды
ENDIF ; конец условного блока

При выполнении примера 7.8 Макроассемблер выберет вариант команды пересылки в зависимости от значения признака variant. Если variant=2, то будет выбрана команда, записанная после ELSE.

Дополнительно отметим, что в условных блоках альтернативный вариант может отсутствовать, если он не нужен, и в обоих вариантах может быть задана не одна, а несколько команд.

Значения признака variant выбраны не случайно. При таких значениях в примерах 7.6 и 7.7 в операциях сдвига можно заменить 1 на имя variant. Тогда в примере 7.6 останется только одна переменная команда, а в примере 7.7 — две. Соответственно, в текст примера 7.6 понадобится включить один условный блок (описанный в примере 7.8), а в текст примера 7.7 — два для выбора вариантов команд rep stosw или rep stosd.

Таким образом, при создании подпрограмм для видеорежимов direct color можно использовать условное ассемблирование и специальные признаки. Это позволяет включать в исходные тексты задач заранее подготовленные заготовки подпрограмм, выполняющих нужные действия.

Трехбайтовый код точки

Такой код не укладывается в общую схему по двум причинам. Во-первых, размер операндов команд не может быть равен трем байтам. Во-вторых, существуют особые точки, код которых расположен в двух смежных сегментах. Поэтому нужны специальные подпрограммы, при составлении которых учитываются особенности трехбайтовых кодов. Две такие подпрограммы, выполняющие запись и чтение кода точки, приведены в примерах 7.1 и 7.2 (см. раздел), первая из них (wrtpnt) нам пригодится.

При описании подпрограмм rdpnt и wrtpnt говорилось, что к ним желательно обращаться только для чтения или записи особых точек, а остальные точки обрабатывать более простым способом. Поэтому мы составим специализированную подпрограмму, которая самостоятельно обрабатывает большинство точек и вызывает wrtpr.t только для записи кодов последних точек видеосегментов.

Текст такой подпрограммы приведен в примере 7.9, перед вызовом адрес первой точки, как обычно, помещается в регистры es:di, а код цвета точки в регистр еах, это сделано для совместимости с четырехбайтовыми режимами.

Пример 7.9. Рисование линии, режим True Color, трехбайтовый код

horline: push bx сохраняем содержимое регистра bx
mov ebx, eax копируем еах в ebx
shr ebx, 16 bx = старшие разряды кода точки
hrln__l : cmp di, -3 это последняя точка в окне 9
jae hrln_3 -> да, особый случай
stosw запись двух младших байтов кода
mov es: [di], Ы запись старшего байта кода
hrln_2 : inc di переадресация операнда
loop hrln 1 управление повторами цикла
pop bx восстанавливаем содержимое bx
ret возврат из подпрограммы
hrln 3: call wrtpnt запись кода особой точки
call NxtWinA установка следующего окна
jrap short hrln 2 короткий переход на метку hrln_2

Для упрощения действий, выполняемых в основном цикле примера 7.9, код точки надо расположить в регистрах ах и. Исходное содержимое регистра сохраняется в стеке, а на его место помещается старшая половина регистра еах, поэтому код красного цвета оказывается в регистре bl.

Основной цикл имеет метку hrin_i и начинается с проверки адреса. Если окажется, что до конца сегмента осталось больше чем 3 байта, то продолжается выполнение основной части цикла. Сначала в видеопамять записываются коды двух младших байтов точки, а затем код старшего байта. Затем производится переадресация операнда, и команда loop управляет повторами цикла. По окончании цикла восстанавливается исходное содержимое регистра bx и происходит возврат на вызывающий модуль.

Особые точки обрабатывает фрагмент примера, имеющий метку hrin_3. Переход на нее происходит в тех случаях, когда до конца видеосегмента остается меньше четырех байтов. При этом вызывается подпрограмма wrtpnt (см. пример 7.1) для записи кода точки, устанавливается следующее окно видеопамяти и происходит переход на метку hrin_2 для продолжения цикла.

Если вам придется работать с видеокартой, поддерживающей в режимах True Color трехбайтовый код точки, то составьте вариант примера 7.6, работающий с таким кодом. Мы не описываем этот вариант потому, что пока подобные видеокарты не получили широкого распространения.

Замечание
Рисование линий сводится к многократно повторяемой записи одного и того же кода в подряд расположенные адреса видеопамяти. В видеорежимах direct color этот код может занимать 2, 3 или 4 байта и от его размера зависит способ записи в видеопамять. Это обстоятельство не позволяет составить одну универсальную подпрограмму для рисования линий в любых видеорежимах VESA. Однако если исключить из рассмотрения трехбайтовый код точки, то можно составить универсальную заготовку подпрограммы, в которой варианты некоторых команд будут выбираться Макроассемблером в зависимости от значения специального признака.

 
Назад Начало Вперед