Главная страница Случайная страница КАТЕГОРИИ: АвтомобилиАстрономияБиологияГеографияДом и садДругие языкиДругоеИнформатикаИсторияКультураЛитератураЛогикаМатематикаМедицинаМеталлургияМеханикаОбразованиеОхрана трудаПедагогикаПолитикаПравоПсихологияРелигияРиторикаСоциологияСпортСтроительствоТехнологияТуризмФизикаФилософияФинансыХимияЧерчениеЭкологияЭкономикаЭлектроника |
Листинг 17.3. Простой двойной параллакс (PARAL1.C).
#include < stdio.h> #include < stdlib.h> #include < string.h> #include < time.h> #include < dos.h> #include " paral.h" char *MemBuf, // указатель на дублирующий буфер *BackGroundBmp, // указатель на битовую карту фона *ForeGroundBmp, // указатель на битовую карту // ближнего плана *VideoRam; // указатель на видеобуфер PcxFile pcx; // структура данных // для чтения PCX-файла int volatile KeyScan; // заполняется обработчиком // прерывания клавиатуры int frames=0, // количество нарисованных кадров PrevMode; // исходный видеорежим int background, // позиция прокрутки фона foreground, //позиция прокрутки битовой карты // ближнего плана position; // общее расстояние прокрутки void _interrupt (*OldInt9)(void); // указатель на обработчик // прерывания клавиатуры BIOS // Функция загружает 256 - цветный PCX-файл int ReadPcxFile(char *filename, PcxFile *pcx) { long i; int mode=NORMAL, nbytes; char abyte, *p; FILE *f; f=fopen(filename, " rb"); if(f==NULL) return FCX_NOFILE; fread(& pcx-> hdr, sizeof(PcxHeader), 1, f); pcx_width=1+pcx-> hdr.xmax-pcx-> hdr.xmin; pcx-> height=1+pcx-> hdr.ymax-pcx-> hdr.ymin; pcx-> imagebytes=(unsigned int) (pcx-> width*pcx-> height); if(pcx-> imagebytes > PCX_MAX_SIZE) return PCX_TOOBIG; pcx-> bitmap= (char*)malloc (pcx-> imagebytes); if(pcx-> bitmap == NULL) return PCX_NOMEM; p=pcx-> bitmap; for(i=0; i< pcx-> imagebytes; i++) { if(mode == NORMAL) { abyte=fgetc(f); if((unsigned char)abyte > 0xbf) { nbytes=abyte & 0x3f; abyte=fgetc(f); if(--nbytes > 0) mode=RLE; } } else if(-—nbytes == 0) mode=NORMAL; *p++=abyte; } fseek(f, -768L, SEEK_END); // получить палитру, из PCX-файла fread(pcx-> pal, 768, 1, f); p=pcx-> pal; for(i=0; i< 768; i++) // битовый сдвиг цветов в палитре *р++=*р > > 2; fclose(f); return PCX_OK; } // Новый обработчик прерывания клавиатуры для программы прокрутки // Он используется для интерактивной прокрутки изображения. // если стандартный обработчик прерывания 9h не будет заблокирован // длительное нажатие на клавиши управления курсором приведет // к переполнению буфера клавиатуры и появлению крайне неприятного // звука из динамика. void _interrupt Newlnt9(void) { register char x; KeyScan=inp(0х60); // прочитать код клавиши x=inp(0x61); // сообщить клавиатуре, что символ обработан outp(0x61, (х|0х80)); outp(0х61, х); outp(0х20, 0х20); // сообщить о завершении прерывания if(KeyScan == RIGHT_ARROW_REL ||// проверка кода клавиши KeyScan == LEFT_ARROW_REL) KeyScan=0; } // Функция восстанавливает исходный обработчик прерываний клавиатуры void RestoreKeyboard(void) { _dos_setvect(KEYBOARD, OldInt9); // восстанавливаем // обработчик BIOS } // Эта функция сохраняет прежнее значение вектора прерывания // клавиатуры и устанавливает новый обработчик нашей программы. void InitKeyboard(void) { OldInt9= _dos_getvect(KEYBOARD); // сохраняем адрес // обработчика BIOS _dos_setvect(KEYBOARD, NewInt9); // устанавливаем новый // обработчик прерывания 9h } // Эта функция использует функции BIOS для установки в регистрах // видеоконтроллера значений, необходимых для работы с цветами, // определяемыми массивом раl[] void SetAllRgbPalette(char *pal) { struct SREGS s; union REGS r; segread(& s); // читаем текущее значение сегментных регистров s.es=FP_SEG((void far*)pal); // в ES загружаем сегмент ра1[] r.x.dx=FP OFF((void far*}pal); // в DX загружаем смещение pal[] r.x.ax=0xl012; // готовимся к.вызову подфункции // 12h функции BIOS 10h r.x.bx=0; /; / номер начального регистра палитры r.х.сх=256; // номер последнего изменяемого регистра int86x(0xl0, & r, & r, & s); // вызов видео BIOS } // Функция устанавливает режим 13h // Это MCGA-совместимыЙ режим 320х200х256 цветов void InitVideo() { union REGS r; r.h.ah=0x0f; // функция Ofh - установка видеорежима int86(0xl0, & r, & r); // вызов видео BIOS PrevMode=r.h.al; // сохраняем старое значение режима r.x.ax=0xl3; // устанавливаем режим 13h int86(0х10, & r, sr); // вызов видео BIOS VideoRam=MK_FP(0xa000, 0); // создаем указатель на видеопамять } //Эта функция восстанавливает исходный видеорежим void RestoreVideo() { union REGS r; r.x, ax=PrevMode; //исходный видеорежим int86(0х10, & r, & r); // вызов видео BIOS } // Функция загрузки битовых карт слоев int InitBitmaps() { int r; // начальное положение линии деления background=foreground=1; // читаем битовую карту фона r=ReadPcxFile(" backgrnd.pcx", & pcx); // проверка на ошибки чтения if(r! = РСХ_ОК) return FALSE; // запоминаем указатель на битовую карту BackGroundBmp=pcx.bitmap; // устанавливаем палитру SetAllRgbPalette(pcx.pal); // читаем битовую карту переднего слоя r=ReadPcxFile(" foregrnd.pcx", & pcx); // проверка на ошибки чтения if (r! = РСХ_ОК) return FALSE; //запоминаем указатель на битовую карту ForeGroundBmp=pcx.bitmap; // создаем буфер в памяти MemBuf=malloc(MEMBLK); // проверка на ошибки распределения памяти if(MemBuf == NULL) return FALSE; memset(MemBuf, 0, MEMBLK); // очистка буфера return TRUE; // все в порядке! } // функция освобождает выделенную память void FreeMem() ( free(MemBuf); free(BackGroundBmp); free(ForeGroundBmp); } // Функция рисует слои параллакса. // Порядок отрисовки определяется координатой слоя по оси Z. void DrawLayers() { OpaqueBlt(BackGroundBmp, 0, 100, background); TransparentBlt(ForeGroundBmp, 50, 100, foreground); } // Эта функция осуществляет анимацию. Учтите, что это наиболее // критичная по времени часть программы. Для оптимизации отрисовки // как сама функция, так и те функции, которые она вызывает, // следует переписать на ассемблере. Как правило, это увеличивает // быстродействие на 100 процентов. void AnimLoop() { while(KeyScan! = ESC_PRESSED) // пока не нажата клавиша ESC ( switch(KeyScan) // определяем, какая клавиша была нажата { case RIGHT_ARROW_PRESSED: //нажата " стрелка вправо" position--; // изменяем позицию if(position < 0) // останавливаем прокрутку, // если дошли до конца { position=0; break; } backgrpund —=1; // прокручиваем фон влево на 2 пикселя if(background < 1) // дошли до конца? background+=VIEW_WIDTH; //...если да - возврат к началу foreground-=2; // прокручиваем верхний // слой влево на 4 пикселя if(foreground < 1) // дошли до конца? foreground+=VIEW_WIDTH; //...если да - возврат к началу break; case LEFT_ARROW_PRESSED: // нажата " стрелка влево" position++; // изменяем текущую позицию прокрутки if(position > TOTAL_SCROLL) // останавливаем прокрутку, // если дошли до конца { position=TOTAL_SCROLL; break; } background+=l; // прокручиваем фон вправо на 2 пикселя if(background > VIEW_WIDTH-1) // дошли до конца? background-=VIEW_WIDTH; //...если да - возврат к началу foreground+=2; // прокручиваем верхний слой // вправо на 4 пикселя if(foreground > VIEW_WIDTH-1) // дошли до конца? foreground-=VIEW_WIDTH; //...если да - возврат к началу break; default: // игнорируем остальные клавиши break; } DrawLayers(); // рисуем слои в буфере в // оперативной памяти memcpy(VideoRam, MemBuf, MEMBLK); // копируем буфер в // видеопамять frames++; // увеличиваем счетчик кадров ) } //эта функция осуществляет необходимую инициализацию void Initialize() { position=0; InitVideo(); // устанавливаем видеорежим 13h InitKeyboard(); // устанавливаем наш обработчик // прерывания клавиатуры if(! InitBitmaps()) // загружаем битовые карты { CleanUp(); //освобождаем память printf(" \nError loading bitmaps\n"); exit(1); } } // функция выполняет всю необходимую очистку void Cleanup() { RestoreVideo(); // восстанавливаем исходный видеорежим RestoreKeyboard(); // восстанавливаем обработчик // прерывания клавиатуры BIOS FreeMem(); // освобождаем всю выделенную память } // Это начало программы. Функция вызывает процедуры инициализации. // Затем читает текущее значение системного таймера и запускает // анимацию. Потом вновь читается значение системного таймера. // Разница между исходным и конечным значениями таймера // используется для вычисления скорости анимации. int main() { clock_t begin, fini; Initialize(}; // проводим инициализацию begin=clock(); // получаем исходное значение таймера AnimLoop(); // выполняем анимацию fini=clock(); // получаем значение таймера CleanUp(); // восстанавливаем измененные параметры printf(" Frames: %d\nfps: %f\n", frames, (float)CLK_TCK*frames/(fini-begin)); return 0; } Оптимизированные версии OpaqueBlt() и TransparentBlt() Листинг 17.4 содержит оптимизированные ассемблерные версии подпрограмм OpaqueBlt() и TransparentBlt(), которые имеются и на дискете (BLIT.ASM). Эти подпрограммы можно использовать вместо соответствующих функций на Си, что увеличит быстродействие программы примерно на 30 процентов. Демонстрационная программа PARAL использует ассемблерные версии этих подпрограмм. Они были написаны с тем расчетом, чтобы могли работать с процессором 286. Поскольку они заполняют буфер системной памяти, их можно оптимизировать и дальше, применив 32-битовые команды перемещения банных.
|