При оформлении рабочей области экрана часто используются одноцветные горизонтальные и вертикальные линии. Способ их рисования зависит от угла наклона и направления линии. Изображение на экране дискретно по своей природе, поэтому гладкими являются только линии, наклоненные под углом кратным 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 (см.