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

         

Построение рисунков с использованием палитры

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

Построение строки рисунка

Для построения строки рисунка в примерах 3.21 и 3.22 вызывалась подпрограмма drawiine. Большинство описанных ранее ее вариантов выполняет копирование кодов точек образа рисунка в видеопамять. Исключением являются примеры 2.17 и 2.18, в которых при построении строки выполняется распаковка 16- и 2-цветных рисунков. В данном случае нам нужен вариант подпрограммы, выполняющий перед записью в видеопамять перекодирование точек образа рисунка по таблице цветов.

Текст подпрограммы приведен в примере 7.21. Перед ее вызовом в регистрах


указываются следующие данные: сх — размер строки, di — адрес ее первой точки в видеопамяти, fs:si — адрес начала рисунка в оперативной памяти, gs — сегмент оперативной памяти, содержащий таблицу цветов. Адрес начала таблицы в этом сегменте подпрограмма выбирает сама из переменной GenOffs. Как обычно, предварительно надо установить окно видеопамяти, к которому относится указанный в di адрес, а в регистре es должен находиться код видеосегмента.

Пример 7.21. Построение строки с перекодированием по таблице цветов

drawline: push eax сохранение содержимого еах
drwlin: lods byte ptr fs:[di] ; al = код точки образа рисунка
and eax, OFFh очистка старших разрядов еах
shl ax, wrdppnt учет размера строки таблицы
add ax, GenOffs ax = смещение начала таблицы
mov ах, дз: [еах] ! ! или mov eax, gs: [eax] для True Color
stosw !! или stosd для True Color
or di, di достигнута граница окна ?
jne @F -> нет, обход следующей команды
cal I NxtWin установка следующего окна
@@: loop drwlin управление повторами цикла
pop eax восстановление содержимого еах
ret возврат из подпрограммы

В цикле примера 7.21 регистр еах используется для указания адреса при чтении кода цвета точки. Адрес формируется в младшем слове регистра, а его старшее слово должно быть очищено. Поэтому после чтения в al кода образа точки старшие разряды регистра еах очищаются с помощью операции "конъюнкция". В зависимости от установленного видеорежима, третья команда сдвигает содержимое ах на 1 или 2 разряда влево. Остается прибавить к ах смещение таблицы в сегменте gs, и адрес требуемого кода будет сформирован. Код считывается в регистр ах (или в еах) и оттуда записывается в видеопамять с помощью строковой операции stosw или stosd. Далее,
как обычно, проверяется адрес видеопамяти и в случае необходимости устанавливается следующее окно. Повторами цикла управляет команда loop.

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

Упрощение подпрограммы

В примере 7.22 приведен текст упрощенного варианта подпрограммы, выполняющей перекодировку и запись одной точки, код которой указан в регистре ai.

Пример 7.22. Перекодировка по таблице и запись точки в видеопамять

wrtpnt: push eax сохранение содержимого еах
and eax, OFFh очистка старших разрядов еах
shl ax, wrdppnt учет размера строки таблицы
add ax, GenOffs ax = смещение начала таблицы
mov ax, gs: [еах] !! или mov eax, gs:[eax] для True Color
stosw !! или stosd для True Color
pop eax восстановление содержимого еах
ret возврат из подпрограммы

Построение небольшого рисунка

Подпрограмма построения рисунка, образ которого помещается в одном сегменте, приведена в примере 7.23.

Перед ее вызовом устанавливается окно видеопамяти, содержащее левый верхний угол рисунка, а адрес этого угла помещается в регистр di. Адрес начала образа рисунка в оперативной памяти записывается в регистры fs:si. Его ширина и высота указываются, соответственно, в dx и сх. Адрес начала таблицы подпрограмма выбирает из переменных GenSeg и GenOffs. Как обычно, должно быть установлено окно видеопамяти, к которому относится указанный в di адрес, а регистр es должен содержать код видеосегмента.

Пример 7.23. Построение рисунка из файла небольшого размера

drawing: PushReg <di,si,bx,ex,gs,Cur_win>; сохранение в стеке
call calloffs вычисление константы offsline
mov gs, GenSeg gs = сегмент таблицы цветов
drwout: push ex сохраняем счетчик повторов
mov . ex, dx задаем размер строки рисунка
call drawline построение очередной строки
pop ex восстанавливаем счетчик повторов
add di, bx коррекция адреса видеопамяти
jnc @F -> адрес в пределах сегмента
call NxtWin установка следующего окна
@@: loop drwout ; управление повторами цикла
PopReg <Cur win,gs,cx,bx,si,di>; восстановление из стека
call setwin ; восстановление исходного окна
ret ; возврат из подпрограммы

Если вы сравните тексты примеров 3.21 и 7.23, то обнаружите, что они различаются только второй и третьей командами, а в списках параметров макровызовов PushReg и PopReg в примере 7.23 добавился регистр gs. Ну и, конечно же, имя drawiine относится к двум разным подпрограммам.

Построение большого рисунка

Если образ рисунка не помещается в одном сегменте, то его приходится считывать из файла и выводить на экран по частям. Поэтому перед началом цикла построения вычисляется размер порции считываемых данных в байтах и количество содержащихся в ней строк. Способ построения рисунка по частям показан в примере 3.22. В него надо внести некоторые изменения, для учета особенностей режимов direct color.

Измененный текст приведен в примере 7.24. Напомним, что в этом случае при вызове явно указывается только адрес начала рисунка в видеопамяти (в регистре di). Размеры рисунка подпрограмма выбирает из переменных iheight и iwidth. Как обычно, должно быть установлено исходное окно видеопамяти, а в регистре es указан код видеосегмента (обычно А000h).

Пример 7.24. Построение рисунка из файла произвольного размера

BigDraw: pusha сохраняем стандартные регистры
PushReg <fs, gs, CL г win> ; сохраняем fs, gs и Cur win
mov fs, SwpSeg fs = сегмент буфера обмена
mov gs, GenSeg gs = сегмент таблицы цветов
mov SwpOffs, 0 нулевой адрес в буфере обмена
xor dx, dx старшая часть делимого dx=0
mov ax , - 1 младшая часть делимого ах=65535
div iwidth ах = 65535 / iwidth
mov part, ax число строк в порции для чтения
mul iwidth количество байтов в порции для чтения
mov nurnbyte , ex сохраняем его в numbyte
mov ax, iheight копируем количество строк в рисунке
mov remline, ax в счетчик не выведенных строк
mov dx, iwidth dx = iwidth, ширина рисунка
call calloffs вычисляем константу переадресации
NewPart: mov ex, numbyte размер порции для чтения
call readf чтение очередной порции данных
jnc sucread -> чтение без ошибок
; Здесь должны выполь яться действия в случае ошибки чтения
sucread : mov ex, part ex = стандартное количество строк
cmp remiine, ex осталось меньше строк ?
jae lbl_l -> нет, обходим команду пересылки
mov ex, remiine сх = оставшееся число строк
1Ы 1: sub remiine, ex уменьшаем оставшееся число строк
xor si, si адрес начала в буфере обмена
drwout : push ex сохраняем счетчик повторов
mov ex , dx задаем размер строки рисунка
call drawline построение очередной строки
pop ex восстанавливаем счетчик повторов
add di, bx адрес начала следующей строки
jnc @F -> адрес в пределах сегмента
call NxtWin установка следующего окна
@@: loop drwout управление повторами цикла
cmp remiine, 0 все строки выведены ?
jne NewPart -> нет, продолжаем построение
PopReg <Cur win, js, fs> ; восстановление Cur win, gs и fs
popa восстановление всех регистров
call setwin восстановление исходного окна
ret возврат из подпрограммы

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

Цикл построения рисунка не изменился, но имя drawline соответствует другой подпрограмме построения строки, поскольку нужна перекодировка точек по таблице цветов.

Учет лишних байтов

В примерах 7.23 и 7.24, так же, как и в примерах 3.21 и 3.22, предполагается, что строки образа рисунка расположены в файле подряд друг за другом. На практике это не всегда так. По разным причинам к каждой строке может быть добавлено некоторое количество байтов, содержимое которых не определено и их нежелательно записывать в видеопамять.

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

Способ определения наличия дополнительных байтов зависит от особенностей формата (или стандарта), которому соответствует файл, содержащий образ рисунка. Общих правил не существует. Как это делается при работе с файлами формата BMP, подробно описано в приложении А.


Содержание раздела