Маскируемый курсор
Маскировка является одним из способов исключения ненужных элементов
изображения в процессе построения рисунка. Она применяется не только при
выводе на экран курсоров и пиктограмм, но и во многих других случаях,
например, при сборке рисунков из отдельных частей. Маска может быть подготовлена
заранее с учетом особенностей рисунка или сформирована динамически, на
основании анализа цветов строящегося рисунка. В данном разделе рассмотрена
работа с готовой маской.
Как производится маскировка
В предыдущем разделе мы использовали тот факт, что при определенных условиях
команда хог инвертирует значение операнда-приемника. Заметим также, что
у этой команды есть еще одно полезное свойство. Вспомним таблицу истинности
логической функции "исключающее ИЛИ" (табл.
5.1). Из нее, в частности, следует, что если один из двух операндов
очищен, то результат выполнения команды хог будет равен значению другого
операнда. Следовательно, при наложении двух цветов с помощью операции
хог черный цвет становится прозрачным.
При построении маскируемого курсора та часть экрана, которую займет
его изображение, предварительно окрашивается в черный цвет (очищается).
С помощью команды хог (или or) на чистом месте можно построить рисунок
любого цвета. Чтобы не портить окружающий фон в образе рисунка, точки,
дополняющие его основную часть до прямоугольника, должны иметь черный
цвет. Это условие обязательно выполняется у стандартных рисунков курсоров
и пиктограмм (см. пример 6.3).
Для закрашивания в черный цвет на место расположения выводимого рисунка
накладывается маска. Как говорилось в разделе ,
байты маски могут содержать только два значения кодов — 00 или OFFh. Маска
является черно-белым рисунком, у которого черные точки соответствуют основной
части маскируемого рисунка, а белые — черным точкам маскируемого рисунка,
дополняющим его заготовку до прямоугольника.
Посмотрите на пример 6.3, и вы увидите, что маска соответствует основному
рисунку.
Фактически маска как рисунок не используется. Она записывается в видеопамять
с помощью логической операции "И" (конъюнкция), которую вычисляет
команда and. При выполнении этой команды операнд приемника будет очищен,
если очищен операнд источника и не изменится, если у операнда источника
установлены все разряды. При маскировке источником являются байты маски,
а приемником — байты видеопамяти. Поэтому после наложения маски очищенными
будут только те байты видеопамяти, которые очищены в маске, а содержимое
остальных не изменится.
Если вы внимательно проанализируете пример 6.3, то обнаружите что находящаяся
в массиве pntmask маска очищает несколько большую часть экрана, чем нужно
для размещения стрелки, хранящейся в массиве pat image. Это сделано для
того, чтобы белая стрелка не потерялась на белом фоне. В результате наложения
такой маски рисунок курсора на экране окажется окруженным черной окантовкой,
и белая стрелка будет видна на любом фоне.
Схема построения рисунка
Для получения изображения маскируемого курсора на экране надо сохранить
исходный фон, наложить на этот фон маску, полученный результат объединить
с заготовкой рисунка с помощью команды хог или or и записать его в видеопамять.
Сохранение исходного фона необходимо потому, что восстановить его после
построения рисунка курсора невозможно. Для размещения сохраняемого фона
в оперативной памяти надо зарезервировать массив pntbuff. Его размер соответствует
размеру рисунка курсора в байтах, т. е. совпадает с размерами массивов
pntimage и pntmask. Каждый из этих трех массивов
занимает в памяти pntxsize pntYsize байтов.
Массив Pntbuff надо расположить в том же сегменте,
в котором находятся pntimage и pntmask. Мы будем считать, что они размещены
в разделе данных задачи и доступ к ним происходит через сегментный регистр
as. При этом буфер описывается так:
pntbuff db 294 dup (?); резервирование 294 байтов в разделе
данных
Если вы расположите массивы в другом сегменте, то в подпрограмму придется
внести незначительные изменения, о чем будет сказано после ее описания.
Порядок расположения массивов не имеет значения, важно чтобы они находились
в одном сегменте.
Подпрограмма Showpnt
Описанные действия выполняются в одном цикле, который повторяется для
каждой точки прямоугольной области, в которой располагается рисунок курсора.
Текст подпрограммы приведен в примере 6.5. Входные параметры явно не задаются,
они находятся в тех переменных, которые были описаны в предыдущем разделе.
Регистр es должен содержать код видеосегмента (хранящийся в vbuff).
Пример 6.5. Подпрограмма построения рисунка маскируемого
курсора
Showpnt: pusha сохранение содержимого регистров
push Cur win сохранение исходного окна
mov ax, Winpnt ах = окно с рисунком курсора
mov Cur win, ax Cur win = Winpnt
call setwin установка исходного окна
xor si, si очистка регистра si
mov di, Offspnt di = адрес в сегменте видеопамяти
mov ex, pntYsize сх = кол-во повторов внешнег о цикла
mov bx, horsize вычисляем константу для
sub bx, pntXsize коррекции адресов строк
Sh_l: push ex сохраняем значение счетчика строк
mov ex, pntXsize ex = количество точек в строке рисунка
Sh_2 : mov al, es: [di] ! ! al = код точки исходного фона
mov pntbuff[si], al ! ! сохраняем его в pntbuf f
and al, pntmask[si] ! ! накладываем маску
xor al, pnt image [si] ! ! формируем новый код точки
stosb ! ! и записываем его в видеопамять
inc si ! ! коррекция адреса рисунка
or di, di адрес в пределах текущего окна ?
jnz @F -> да, обходим следующую команду
call nxtwin установка следующего окна
@@: loop sh 2 управление повторами цикла
pop ex восстанавливаем счетчик строк
add di, bx корректируем адрес видеопамяти
jnc @F -> адрес в пределах текущего окна
call nxtwin установка следующего окна
@@: loop sh 1 управление внутренним циклом
pop Car win восстановление значения Cur win
popa восстановление всех регистров
call setwin восстановление исходного окна
ret возврат из подпрограммы
Начало примера 6.5 отличается от примера 6.4 только одной командой.
Вместо записи адреса массива pntimage в регистр si последний просто очищается.
Это сделано потому, что регистр si используется для доступа к трем массивам,
а не к одному, как это было в примере 6.4.
Принципиальное различие между примерами в основных действиях, выполняемых
во внутреннем цикле, который в данном случае имеет метку sh_2.
Первая команда внутреннего цикла считывает код очередной точки из видеопамяти
в регистр al, а вторая сохраняет его в очередном байте массива pntbuf
f. После этого в регистр al помещается результат вычисления логической
функции "И" от исходного значения регистра и содержимого очередного
байта маски. В зависимости от кода байта маски регистр al либо будет очищен,
либо его исходное значение не изменится — третьего не дано. Следующая
команда завершает формирование нового кода точки, она вычисляет логическую
функцию "исключающее ИЛИ" от содержимого регистра al
и очередного байта массива pntimage. Остается записать новый код
точки в видеопамять, что и делает команда stosb, одновременно она увеличивает
содержимое регистра di на 1.
Основные действия выполнены, шестая команда увеличивает на 1 адрес оперативной
памяти (содержимое регистра si). Адрес видеопамяти (содержимое di) увеличила
команда stosb, но надо проверить, остался он
в пределах текущего видеосегмента или нет. Признаком выхода за пределы
сегмента является нуль в регистре di, при этом подпрограмма nxtwin установит
следующее окно видеопамяти. Повторами внутреннего цикла управляет команда
:оор Sh_2.
После построения строки из стека восстанавливается значение счетчика
повторов, вычисляется адрес начала следующей строки в видеопамяти, команда
loop sh_i повторяет выполнение внешнего цикла
до тех пор, пока на экран не будут выведены все строки изображения курсора.
В заключение из стека выталкиваются исходные значения переменной
cur_wir. У всех регистров, восстанавливается исходное окно видеопамяти
и происходит возврат на вызывающий модуль.
Массивы в другом сегменте. Текст примера 6.5 составлен из расчета па
то, что массивы pntimage, pntmask и pntbuff
расположены в разделе данных п для доступа к ним используется регистр
ds, имя которого не указывается перед операндами. Если вы предпочитаете
расположить указанные массивы в другом сегменте, то в трех командах примера
6.5 перед именами массивов надо явно указать имя выбранного вами сегментного
регистра. Например, mov fs:pntbuf f [si], ai, если
для доступ а к массиву pntbuff используется сегментный регистр
fs.
Важно
Еще раз напоминаем, что все три массива должны располагаться в одном сегменте.
Действия при удалении курсора. Перед перемещением курсора его старое изображение
удаляется с экрана. Кроме того, изображение курсора удаляется перед выводом
на экран новых рисунков. В разделе
было описано, в каких случаях и почему это надо делать.
Для удаления изображения курсора надо восстановить исходный фон, сохраненный
при его построении. Эта процедура ничем не отличается от построения небольшого
рисунка, образ которого находится в оперативной памяти, только образом
рисунка является исходный фон на месте, построения изображения курсора.
В примере 3.21 (см. раздел) описана
подпрограмма построения небольшого рисунка. При выполнении графических
задач курсор перемещается достаточно часто, поэтому для восстановления
исходного фона лучше составить специальную подпрограмму, а не использовать
одну из общедоступных. Если же задача работает с указателем мыши в режиме
прерываний, то без специальной подпрограммы просто не обойтись. Поэтому
мы перепишем пример 3.21 применительно к данному случаю.
Подпрограмма Hidepnt. Текст подпрограммы, выполняющей удаление изображения
курсора, приведен в примере 6.6. В нем использованы те же переменные,
что и в примерах 6.4 и 6.5, а из трех массивов нужен только pntbuff,
содержащий ранее сохраненный фон.
Пример 6.6. Восстановление исходного фона на месте рисунка
курсора
Hidepnt pusha сохранение содержимого регистров
push Cur win сохранение исходного окна
mov ax, Winpnt ах = окно с рисунком курсора
mov Cur win, ax Cur win = Winpnt
call setwin установка исходного окна
lea si, pntbuff si = адрес буфера с сохраненным фоном
mov di, Offspnt di = адрес в сегменте видеопамяти
mov ex, pntYsize сх = кол-во повторов внешнего цикла
mov bx, horsize вычисляем константу для
sub bx, pntXsize коррекции адресов строк
hid 1: push ex сохраняем значение счетчика строк
mov ex, pntXsize сх = количество точек в строке рисунка
hid 2: movsb ! ! копируем байт из pntbuff в видеопамять
or di, di конец сегмента видеопамяти ?
jnz @F -> нет, обходим следующую команду
call nxtwin устанавливаем следующее окно
@@: loop hid 2 управление повторами цикла
pop ex восстанавливаем счетчик строк
add di , bx корректируем адрес видеопамяти
jnc @F -> адрес в пределах текущего окна
call nxtwin установка следующего окна
@@: loop hid_l управление повторами цикла
pop Cur win восстановление значения Cur win
popa восстановление всех регистров
call setwin восстановление исходного окна
ret возврат из подпрограммы
В примере 6.6 выполняются действия, которые уже неоднократно обсуждались,
поэтому мы опустим его подробное описание. Автор надеется, что читатель
разберется в том, что делают конкретные команды.
Сравнение способов построения. В заключение раздела оценим, что мы получаем
и что теряем при работе с маскируемым курсором. Бесспорное преимущество
подпрограммы showpnt в том, что возможна работа с изображением курсора,
цвет которого зависит только от самого рисунка и не зависит от находящейся
на экране картинки. Собственно говоря, ради этого и применяется маскировка.
Но при этом, объем оперативной памяти, необходимый для хранения рабочих
массивов, увеличился в три раза. При описанном способе построения изображения
курсора сократить его невозможно. Попробуйте самостоятельно ответить на
вопрос — почему нельзя временно сохранять исходный фон в массиве pntimage
и восстанавливать его содержимое при восстановлении исходного фона?
При построении немаскируемого курсора обработку кода каждой точки выполняли
2 команды внутреннего цикла. В примере 6.5 таких команд стало 5, и сократить
их количество невозможно.
Наконец, вместо одной подпрограммы Tgipn.tr при
использовании маски нужны две — showpr.t и Hidepr.t.
Объединить их в одну подпрограмму невозможно. Включение и выключение
курсора это два независимых процесса, которые не могут выполняться одновременно.
Такова реальная плата за улучшение качества изображения и возможность
работы с цветным рисунком курсора. Выбор маскировки или отказ от нее зависит
от вас. |