В файлах формата PCX образы полноцветных рисунков обычно хранятся в упакованном виде. Эффективность принятого в стандарте PCX способа упаковки не высока, но иногда получаются удовлетворительные результаты.
В данном разделе описана подпрограмма, выполняющая построение упакованного полноцветного рисунка произвольного размера. Как вы сможете убедиться, она во многом совпадает с подпрограммой аналогичного назначения, описанной в примере 3.26. Это объясняется тем, что вспомогательные действия выполняются в подпрограммах распаковки и построения строк. Поэтому мы начнем с рассмотрения способа распаковки.
Заголовок файла стандарта PCX имеет фиксированный эазмер soh байтов. Назначение его основных полей (байтов и слов) описано в разделе. Здесь нас интересует еще одно поле, которое там не упоминалось.
В байте со смещением 41h указано количество цветов (битовых плоскостей),
на которое разложена каждая строка образа рисунка. Если оно равно 3, то
Напоминаем
В байте со смещением 2 хранится "ключ кодирования", значение
которого может быть равно 0 или 1. В первом случае образ рисунка не упакован,
а во втором упакован по способу RLE, который описан в разделе.
Способ распаковки строки. Перед упаковкой строка рисунка разлагается
на три подстроки, размер каждой из которых равен размеру строки. Одна
из них содержит коды красного базового цвета, другая — зеленого и третья
— синего. Другими словами, производится разложение строки по компонентам
базовых цветов. Затем каждая подстрока упаковывается по способу RLE, и
результат записывается в файл. Если исходный рисунок состоял из к строк,
содержащих трехбайтовые коды точек, то упакованный рисунок содержит зк
групп однобайтовых кодов цвета. Зачем это делается, описано в следующем
разделе, а здесь нас интересует способ распаковки.
Итак, в файле формата PCX, содержащем упакованный полноцветный рисунок,
начиная с адреса 80h, записаны группы упакованных однобайтовых кодов базовых
цветов в такой последовательности:
коды красного базового цвета первой строки рисунка
коды зеленого базового цвета первой строки рисунка
коды синего базового цвета первой сроки рисунка
коды красного базового цвета второй строки рисунка
коды зеленого базового цвета второй строки рисунка
и т. д. вплоть до коды синего базового цвета последней строки рисунка
В упакованном виде размеры групп переменные, но после распаковки каждая из них состоит из N байтов, где N — количество точек в строке рисунка. При распаковке первых трех групп будут получены базовые цвета всех точек первой строки рисунка. При распаковке следующих трех групп — второй строки рисунка и т. д.
Таким образом, для получения цветов всех точек одной строки рисунка цикл распаковки повторяется три раза. Однако если результаты записывать в подряд расположенные байты памяти, то базовые цвета одной точки будут отстоять друг от друга на N байтов и это придется учитывать при пересылке распакованного образа рисунка в видеопамять.
Для того чтобы не усложнять подпрограммы построения строки, надо изменить порядок записи результатов распаковки так, чтобы базовые цвета одной точки оказались в трех подряд расположенных байтах оперативной памяти. Другими словами, подпрограмма распаковки должна восстанавливать формат rgb или bgr, по усмотрению программиста. Мы выберем формат bgr для того, чтобы при построении строки можно было использовать подпрограммы примера 7.26.
Подпрограмма Unpack
Текст подпрограммы распаковки строки рисунка с преобразованием в формат bgr показан в примере 7.27. Распакованная строка записывается в свободную часть буфера общего назначения, поэтому перед вызовом подпрограммы в регистр gs копируется содержимое переменной Genseg. Адрес в буфере общего назначения подпрограмма выбирает из переменной GenOffs. Для чтения байтов упакованной строки вызывается вспомогательная подпрограмма Nxt_sym (см. пример 3.25, раздел 3.3.3), которая помещает в регистр ai код очередного байта из буфера обмена и следит за тем, чтобы этот буфер не был пустым.
Пример 7.27. Распаковка строки и преобразование в формат bgr
Unpack: PushReg <ax, dx, ex, di> сохранение используемых
регистров
mov di, GenOffs адрес начала строки в GenSeg
add di, 02 начинаем с записи красных цветов
mov ex, 03 количество цветов
Unpckl: PushReg <di,cx> сохранение содержимого di и сх
mov dx, f width логический размер строки
Unpck2 : call nxt sym возвращает в al — текущий код
rnov ex, 01 предполагаем одиночный символ
cmp al, OCOh одиночный символ ?
jbe Unpck3 => да, на запись символа
mov ex, ax пересылка ах в счетчик
and ex, 3Fh выделяем число повторов
call Nxt sym читаем повторяемый код
Unpck3 : sub dx, ex кол-во байтов до конца строки
Unpck4 : mov gs: [di] , al запись кода цвета
add di, 03 коррекция адреса
loop Unpck4 управление повторами записи
Подпрограмма примера 7.27 состоит из двух вложенных циклов, внешний имеет метку Unpckl, а внутренний unpck2.
Внутренний цикл отличается от аналогичного цикла unpioop примера 3.24
тем, что запись результатов распаковки выполняется в цикле, имеющем метку
unpck4 и состоящем из трех команд. Он усложнен потому, что результаты
распаковки записываются в память не подряд друг за другом, а с шагом в
3 байта.
Внешний цикл управляет трехкратным повторением внутреннего. Кроме того,
он формирует в регистре di адрес для записи результатов так, чтобы строка
соответствовала формату bgr.
Подпрограмма PackDrw
Перед построением рисунка надо прочитать заголовок файла, проверить его соответствие стандарту PCX и наличие в нем полноцветного упакованного рисунка. Затем из полей заголовка выбираются значения переменных iheight, iwidth и fwidth. Если заголовок прочитан полностью (soh байтов), то файл установлен на начало образа рисунка.
Текст подпрограммы приведен в примере 7.28. Перед ее вызовом устанавливается окно видеопамяти, в котором расположено начало строящегося рисунка. Адрес начала рисунка указывается в регистре di, а регистр es должен содержать код видеосегмента (доешь). Кроме того, надо удалить с экрана изображение курсора (call Hidepnt) и восстановить его на экране (call Showpnt) после построения рисунка.
Пример 7.28. Построение упакованного рисунка формата PCX
or dx, dx обработана вся строка ?
jnz Unpck2 => нет, продолжение обработки
PopReg <cx,di> восстановление содержимого di и сх
dec di для записи зеленых или синих цветов
loop Unpckl управление внешним циклом
PopReg <di, ex, dx, ax> восстановление регистров
ret возврат из подпрограммы
I PackDrw : pusha сохранение "всех" регистров
PushReg <f s , gs , Cur win> ; сохранение fs, gs и Cur win
mov gs, GenSeg gs = сегмент общего назначения
mov f s , SwpSeg fs = сегмент буфера обмена
xor si, si адрес начала буфера обмена
mov SwpOffs, si адрес начала буфера обмена
mov incount, si incount = 0 — буфер обмена пуст
mov dx, iwidth dx = количество точек в строке рисунка
1 mov ex, iheight сх = количество строк в рисунке
call calloffs bx -- константа для коррекции адресов
make: push ex ; сохраняем счетчик повторов цикла
call Unpack распаковка очередной строки
PushReg <fs,si> сохраняем содержимое fs и si
Ifs si, dword ptr GenOffs; fs:si = адрес распакованной строки
raov ex, dx сх = количество точек в строке рисунка
call drawline построение очередной строки
add di , bx адрес начала следующей строки
jnc @F ; -> адрес в пределах текущего сегмента
call Nxtwin установка следующего окна
@@: PopReg <s i , f s , cx> восстанавливаем содержимое si, fs и
сх
loop make управление повторами цикла
PopReg <Cur win,gs, fs> ; восстановление Cur win, gs и fs
popa восстановление "всех" регистров
call Setwin восстановление исходного окна
ret возврат из подпрограммы
Выполнение примера 7.28 начинается с сохранения в стеке содержимого
всех регистров, а также сегментных регистров fs, gs и переменной cur_win.
Затем в gs и fs записываются коды сегментов буферов GenSeg и SwpSeg и
очищаются регистр si, переменные swpoffs и incount. В регистры dx и сх
копируются размеры рисунка, и вызывается подпрограмма caiioffs для вычисления
константы переадресации строк.
Основной цикл примера 7.28 имеет метку make. Для построения каждой строки
в нем последовательно вызываются подпрограммы unpack и drawline. Перед
вызовом drawline сохраняется исходное содержимое пары регистров fs:si,
а в них помещается адрес распакованной строки. После возвращения из drawline,
как обычно, корректируется адрес видеопамяти, восстанавливается содержимое
регистров si, fs, сх и команда loop make управляет повторами цикла.
Перед возвратом на вызывающий модуль восстанавливаются все сохраненные
в стеке величины и исходное окно видеопамяти. Завершает подпрограмму команда
ret.
В зависимости от видеорежима в примере 7.28 используется один из вариантов
подпрограмм drawline, описанных в примере 7.26.
Рисунки, использующие палитру
Подпрограмма PackDrw позволяет строить рисунки, подготовленные с применением палитры цветов. Для распаковки таких рисунков в ней вызывается подпро!рамма unpack, описанная в примере 3.26, а вариант подпрограммы drawline выбирается в зависимости от установленного видеорежима.
При работе в режимах PPG для построения распакованной строки вызывается одна из подпрограмм drawline, описанных в разделе.
Если же задача работает в одном из режимов direct color, то перед построением рисунка хранящаяся в файле палитра преобразуется в таблицу цветов. В зависимости от видеорежима для этого выбирается одна из подпрограмм 7.17—7.20. Строку рисунка с перекодированием точек по таблице цветов строит подпрограмма примера 7.21.