ЭФФЕКТИНОЕ ПРОГРАММИРОВАНИЕ В KOLIBRI OS
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>><<<<<<<<<<<<<<<<<<<<<<<<<<<<<
Автор
- Теплов Алексей. icq:267994076
e-mail: tay-ugur@chel.surnet.ru
INTRO - ВСТУПЛЕНИЕ
Написать сборник статей на тему
Эффективное программирование в КОЛИБРИ
меня побудило то, что
большинство рассмотренных мной программ не всегда эффективно
использовали
возможности системы.
В большинстве своем, задача решалась по пути наименьшего сопротивления,
что безусловно не
может радовать. Многие методы в своей структуре достаточно ошибочны или
неверны, или, в лучшем
случае отражают только одну сторону многогранной действительности...
Я далеко не самый лучший программист, наверное я как и все - просто
ищет истину, а она где-то рядом
======================================
Сегодня я постараюсь описать как можно вывести рамку в рабочее поле
программы т.е. на канву окна приложения. Зачем это нужно? Так вот,
очень часто я встречаю в коде конструкции, кода когда для того что бы
изменить или перерисовать кнопку или еще что на канве, прибегают к
такому коду: Изменяют значение битового состояния переменной,
ответственной за то или иное состояние объекта и без зазрения совести
перерисовывают полностью окно. Из-за такого кода окно вынужденно часто
моргать, что очень раздражает!
Сегодня я расскажу, как можно грамотно выйти из данной ситуации, с
наименьшими потерями. Решение напрашивается само по себе - это при
изменении объекта перерисовать его индивидуально!
Рассмотрим несколько вариантов того как это можно сделать
НЕ
ОПТИМАЛЬНЫЙ ВАРИАНТ:
Рисование закрашеного прямоугольника системой, происходит, как
рисование
набора линий. По этому данный вариант будет выполняться медленее чем
индивидуальная прорисовка рамки с помощью линий!
1) Нарисовать черный прямоугольник, затем прямоугольник
светлый(цвет
фона) меньшего размера, получится
рамка черного цвета или же того который вы зададите. Вот собственно и
идея. Рассмотрим вопрос, с реализацией перерисовки прямоугольников, при
изменении положения окна.
1) При перемещении, активного окна, по области рабочего стола
перерисовывать как бы и не за чем, т.к. нет перекрытия. Однако, у нас
есть одно системное событие, которое нас информирует об изменении
положения окна и тут мы можем только постараться перерисовать только
то, что необходимо. У нас на канве окна нарисованы 2 объекта. 1 -
это рамка, состоящая из 2-х прямоугольников, которые рисуются
последовательно, сначала рисуется черный прямоугольник, затем рисуется
прямоугольник цвета фона в области черного прямоугольника, тем самым
достигается получение рамки.
Элементы управления в данной программе реализованы так:
- D or d - прорисовка первой рамки
- 1 - прорисовка черного квадрата
- 2 - прорисовка квадрата цвета фона.
- C or c - очистка битовой матрицы состояния прямоугольников.
Толщину рамки можно варьировать т.к. она задается с помощью
размеров прямоугольника имеющего цвет фон, а он располагается внутри
черного прямоугольника. Объект № 2 это, по сути, те же 2
прямоугольника,
но для наглядности прорисовкой их можно управлять, с помощью клавиш,
т.е. при нажатии 1 происходит прорисовка черного прямоугольника, а при
нажатии 2 происходит прорисовка фонового прямоугольника и тем самым
получается рамка.
В данной программе рассмотрен
эффективный метод прорисовки на канве окна рамок с помощью
прямоугольников, причем только в тех случаях в каких это не обходимо.
Для этого используется переменная, в которой храниться битовое
состояние объектов, т.е. при происхождении события в данной переменной
устанавливается бит в соответствии с произошедшим событиям. В данном
случае используется переменная draw_bt_ramka, в которой и храниться
состояние объектов. Рассмотрим, за что отвечает каждый бит в данной
переменной.
- 0 бит отвечает за состояние прорисовки первой рамки, т.е. самой
первой 0 - не нарисовано 1 - нарисовано
- 1 бит отвечает за состояние прорисовки черного прямоугольника для
второй рамки т.е. 0 - не нарисовано 1 - нарисовано
- 2 бит отвечает за состояние прорисовки фона прямоугольника для
второй рамки т.е. 0 - не нарисовано 1 - нарисовано
Теперь разберем подробнее логику, когда можно прорисовывать, а когда
нет.
Рассмотрим возможные состояния нарисованных прямоугольников.


- Нарисован 2-й черный квадрат



Разработаем метод управления данными событиями:
1) При прорисовке первой рамки будем устанавливать 0 бит в 1 переменной
draw_bt_ramka.
2) При прорисовке для второй рамки черного прямоугольника будем
устанавливать 1-ый бит переменной draw_bt_ramka в 1, а второй бит
переменной draw_bt_ramka будем устанавливать в 0. т.к. если мы это не
сделаем, то при очередном вызове процедуры рисования рамки у нас
отобразиться и черный прямоугольник и фоновый прямоугольник, если он
был уже до этого раз прорисован.
3) При прорисовке фонового прямоугольника получается рамка и
устанавливаем бит 2 переменной draw_bt_ramka в 1, т.к. он за это
отвечает. Все же рассмотрим такой вариант, когда на пустом месте т.е
где нет ни рамки второго прямоугольника, ни черного квадрата
пользователь нажимает 2 т.е. нарисовать фон, если мы это сделаем то мы
потратим время на перерисовку фона который уже у нас нарисован. Что бы
это не случилось проверим 1 бит переменной draw_bt_ramka, который
отвечает за прорисовку черного прямоугольника, и если он равен 0, то
нам просто не зачем рисовать фон т.к. это просто потеря времени и
нерациональное программирование. Произведем проверку, и если
необходимо, если 1 бит переменной draw_bt_ramka равен 1, - нарисован
черный фон, т.е.
мы прорисуем фоновый прямоугольник, тем самым у нас получится рамка.
Посмотрим на код который это делает:
;>>>>>>>>>>>>>>>>>>>>>>>>>>>
draw_ramk:
call
draw_ramka
; рисование 2-х прямоугольников т.е. рамки
or
[draw_bt_ramka],0x1 ;
установка 0 бита в 1 т.е. теперь по 0 биту нашей переменной установлен
бит в 1
jmp still
draw_ramk1:
call draw_ramka1
and
[draw_bt_ramka],0x3 ;сбросим 3-й бит в 0 т.к. он
ответственный за прорисовку второй рамки
or
[draw_bt_ramka],0x2 ; если не установлен наш
бит, то мы его установим установка
1 бита в 1 т.е. теперь по 1 биту нашей переменной установлен бит в 1
jmp still
draw_ramk2:
test [draw_bt_ramka],2 ;
проверка 1 бита если он =0, то нам не нужно ничего делать зачем
нам рисовать фон ?
jz
still ; если ZF=1 то мы просто выйдем
call draw_ramka2
or
[draw_bt_ramka],0x4 ; установка
3 бит в 1 т.е. теперь по 3 биту нашей переменной установлен бит в 1
jmp still
;>>>>>>>>>>>>>>>>
Возможен еще такой вариант, когда у нас уже нарисована вторая рамка и
пользователь еще раз нажимает 2 - отрисовать фон, в данном случае можно
запретить рисование фона т.к. он уже нарисован, но все же я не стал
этого делать. Я тем самым ограничиваю управление, и, поскольку в данном
случае это лишь пример, я оставил данную возможность для наглядности.
В самой первой редакции кода было сделано, что проверку рисовать или же
нет проверялась в процедуре рисования окна @PROV_BIT_AND_DRAW(т.е. draw_window
т.к. вызов именнно из неё), но этот подход я пересмотрел т.к. в вызове
draw_window необходимо максимально быстро выполнить иначе может
возникнуть "эффект моргания". В данной процедуре организована только
проверка состояния битов переменной и вывод в соответствии с этим
прямоугольников.
---------------------------------------
+ Очень компактный код.
- Усложняется в целом структура программы.
______________________________________
пример
кода
;Эффективное программирование в
KOLIBRI №1
;<Lrz>
;Рисование рамки методом
прямоугольников дата 12 апреля 2006 года.
use32
org 0x0
db 'MENUET01'
dd 0x1
dd start
dd i_end
dd 0x10000
dd 0x8000
dd 0x0
dd 0x0
;Область кода
start:
red_win:
call draw_window
still:
mov eax,10 ;ждать события
int 0x40
cmp eax,1 ;если изменилось
положение окна
jz red_win
cmp eax,2 ;если нажата клавиша то
перейти
jz key
cmp eax,3 ;если нажата кнопка то
перейти
jz button
jmp still ;если ничего из
перечисленного то снова в цикл
button:
mov eax,17
int 0x40
cmp ah,1
jnz still
or eax,-1 ;в
eax,-1 - 5 ,байтов у нас же только 3
int 0x40
;далее выполняется выход из программы
key:
mov eax,2
int 0x40
or ah,0x20 ;or al,20h" переводит заглавные латинские
буквы в маленькие, а цифры оставляет на месте.
cmp ah,'d'
jz draw_ramk
cmp ah,'1'
jz draw_ramk1
cmp ah,'2'
jz draw_ramk2
cmp ah,'c'
jz @CLEAR
jmp
still;>>>>>>>>>>>>>>>>>>>>>>>>>>>
@CLEAR:
mov [draw_bt_ramka],0 ;обнуление битовой матрицы
объектов нужно для демонстрации
jmp still
;>>>>>>>>>>>>>>>>>>>>>>>>>>>
draw_ramk:
call draw_ramka
; рисование 2-х прямоугольников
т.е. рамки
or [draw_bt_ramka],0x1 ; установка 0 бита в 1 т.е.
теперь по 0 биту нашей переменной установлен бит в 1
jmp still
draw_ramk1:
call draw_ramka1
and [draw_bt_ramka],0x3 ;сбросим
3-й бит в 0 т.к. он ответственный за прорисовку второй рамки
or [draw_bt_ramka],0x2 ;
если не установлен наш бит, то мы его установим установка 1 бита в 1
т.е. теперь по 1 биту нашей переменной установлен бит в 1
jmp still
draw_ramk2:
test [draw_bt_ramka],2 ; проверка 1 бита если
он =0, то
нам не нужно ничего делать зачем нам рисовать фон ?
jz still ; если
ZF=1 то мы просто выйдем
call draw_ramka2
or [draw_bt_ramka],0x4 ; установка 3 бит в 1 т.е.
теперь по 3 биту нашей переменной установлен бит в 1
jmp still
;>>>>>>>>>>>>>>>>
draw_ramka: ;метод
прямоугольников - рисование рамки двумя прямоугольниками
mov eax,13
xor edx,edx
mov ecx,25*65536+66
mov ebx,25*65536+76
int 0x40
mov edx,0x00AABBCC
mov ecx,26*65536+64
mov ebx,26*65536+74
int 0x40
ret
draw_ramka1: ;метод
прямоугольников - рисование рамки двумя прямоугольниками
mov eax,13
xor edx,edx
mov ecx,25*65536+66
mov ebx,125*65536+76
int 0x40
ret
draw_ramka2: ;метод
прямоугольников - рисование рамки двумя прямоугольниками
mov eax,13
mov edx,0x00AABBCC
mov ecx,26*65536+64
mov ebx, 126*65536+74
int 0x40
ret
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
draw_window:
mov eax,12
xor ebx,ebx
add ebx,1
xor eax,eax
mov ebx,50*65536+480
mov ecx,30*65536+100
mov edx,0x00AABBCC
mov esi,0x805080DD
mov edi,0x005080DD
int 0x40
mov eax,8
mov ebx,(480-17)*65536+12
mov ecx,4*65536+12
xor edx,edx
add edx,1
mov esi,0x6688DD
int 0x40
shr eax,1 ;то же но сдвиги быстрее т.е.
получается 8 разделить на 2 (на 386 процессорах)
;mov eax,4 ;если предполагается использовать
скалярный процессор, то можно использовать данню инструкцию
mov ebx,(480-13)*65536+7
mov ecx,0x00DDEEFF
mov edx,0x2B
xor esi,esi
add esi,1
int 0x40
mov ebx,(400-30)*65536+7
mov edx,0x2D
int 0x40
mov ebx,8*65536+8
mov ecx,0x10DDEEFF
mov edx,hed
mov esi,i_end - hed
int 0x40
mov ebx,205*65536+25
xor ecx,ecx
mov edx,mess1
mov esi,mess1_end - mess1
int 0x40
loop_s:
add ebx,10
add edx,mess1_end - mess1
int 0x40
cmp ebx,205*65536+25+60
jb loop_s
call
@PROV_BIT_AND_DRAW ; проверка битов на перерисовку
mov eax,12
mov ebx,2
int 0x40
ret
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
@PROV_BIT_AND_DRAW: ; проверка
битовой матрицы и перерисовка в соответствии с ней объектов т.е.
прямоугольников
mov al,[draw_bt_ramka] ; загрузим значение
переменой в которой наше битовое состояние нарисованных прямоугольников
т.к. с регистром обращаться более эффективнее
test al,0x1 ; т.к. возможны варианты
0,1,2,3,4,5,6,7 и того 8 значений, хотя тут мы проверяем биты
jnz draw_@r ;отрисовка первого прямоугольника
@_bt1: test
al,0x2
jnz draw_@r1 ;отрисовка черного (фона)
прямоугольника
@_bt2: test
al,0x4
jnz draw_@r2 ;отрисовка белого прямоугольника
ret
draw_@r:
push eax
;сохраним нашу битовую матрицу состояния объектов
call draw_ramka ; рисуем
черный и белый прямоугольник т.е. получается рисуем прямоугольную
рамку черного цвета
pop eax
;восстановим битовую матрицу состояния объектов
jmp @_bt1
draw_@r1:
push eax
call draw_ramka1 ;Рисуем только
черный прямоугольник
pop eax
jmp @_bt2
draw_@r2:
call draw_ramka2 ; рисуем фон в
чёрном прямоугольнике
ret
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
;DATA данные
draw_bt_ramka dd 0 ; т.к. btr требует минимум 16 бит переменная в
которой храниться битовое состояние объектов
mess1 db 'Данная программа демонстрирует метод построе-'
mess1_end:
mess2 db 'ния рамки прямоугольником. Для демонстрации '
mess3 db 'возможностей вам необходимо нажать D or d и '
mess4 db 'цифры 1 и 2 произойдет прорисовка рамки и '
mess5 db 'демонстрация построения. C or с - очищает би-'
mess6 db 'товую матрицу состояния прямоугольников, мож-'
mess7 db 'но убедиться, перекрыв это окно другим окном.'
hed db 'Less1 - пример рисования рамки методом прямоугольников'
i_end:
++++++++++++++++++++++++
2) Нарисовать прямоугольную рамку по точкам. Данный метод достаточно
сложен в реализации и скорость выполнения его будет низкой т.к.
нужно будет вывести очень много точек.
3) Нарисовать прямоугольную рамку с помощью линий. Данный метод
является самым оптимальным т.к. в данном случае затраты времени
процессором на вывод рамки будут минимальными т.к. нам нужно всего лишь
нарисовать несколько линий. В коде данного приложения нет ничего
сложного. После прорисовки рамки происходит установка соответствующего
бита в переменной. Когда же при перерисовке окна возникает вопрос что
же нарисовать, то рисование происходит в соответствии с битовой
матрицей стостояния рамок. Код приложения почти повторяет код, который
расположен выше.
______________________________
пример
кода
;Эффективное программирование в
KOLIBRI №1-2
;<Lrz> - Теплов Алексей
;Рисование рамки методом прямых линий
дата 18 апреля 2006 года.
use32
org 0x0
db 'MENUET01'
dd 0x1
dd start
dd i_end
dd 0x10000
dd 0x8000
dd 0x0
dd 0x0
;Область кода
start:
red_win:
call draw_window
still:
mov eax,10 ;ждать события
int 0x40
cmp eax,0x1 ;если изменилось
положение окна
jz red_win
cmp eax,0x2 ;если нажата клавиша
то перейти
jz key
cmp eax,0x3 ;если нажата кнопка то
перейти
jz button
jmp still ;если ничего из
перечисленного то снова в цикл
button:
mov eax,17
int 0x40
cmp ah,1
jnz still
or eax,-1 ;в
eax,-1 - 5 ,байтов у нас же только 3
int 0x40 ;далее выполняется выход из программы
key:
mov eax,2
int 0x40
or ah,0x20 ;or al,20h" переводит заглавные латинские
буквы в маленькие, а цифры оставляет на месте.
cmp ah,'d'
jz draw_ramk
cmp ah,'1'
jz draw_ramk1
cmp ah,'2'
jz draw_ramk2
cmp ah,'c'
jz @CLEAR
jmp still
;>>>>>>>>>>>>>>>>>>>>>>>>>>>
@CLEAR:
mov [draw_bt_ramka],0x0 ;обнуление битовой матрицы
объектов нужно для демонстрации
jmp still
;>>>>>>>>>>>>>>>>>>>>>>>>>>>
draw_ramk: ;
отрисовать рамку
call draw_ramka
or [draw_bt_ramka],0x1 ; установить бит 0 в 1-цу
данной переменной т.е. при перерисовке окна будет нарисована рамка
jmp still
draw_ramk1:
call draw_ramka1
or [draw_bt_ramka],0x2 ;
если не установлен наш бит, то мы его установим
jmp still
draw_ramk2:
call draw_ramka2
or [draw_bt_ramka],0x4
jmp still
;>>>>>>>>>>>>>>>>
draw_ramka: ;метод прямых -
рисование рамки двумя прямыми линиями
mov eax,38
mov ebx,5*65536+76
;нарисуем верхнюю черную линию
mov ecx,25*65536+25
xor edx,edx
int 0x40
mov ecx,90*65536+90
;нарисуем нижнюю черную линию
int 0x40
mov ebx,5*65536+5
mov ecx,25*65536+90
;нарисуем боковую левую черную линию
int 0x40
mov ebx,76*65536+76
;нарисуем боковую правую черную линию
int 0x40
;отрисовка вторых повторных белых линий
mov ebx,6*65536+75
mov ecx,26*65536+26 ;верхная
mov edx,0x00E0E0E0
int 0x40
add ebx,2
mov ecx,91*65536+91 ;нижная больше
верхней на пиксель
int 0x40
mov ebx,6*65536+6
mov ecx,26*65536+89 ;боковая левая
int 0x40
add ecx,1
mov ebx,77*65536+77 ;бокавая правая
int 0x40
ret
draw_ramka1: ;метод прямых -
рисование рамки двумя прямыми линиями
mov eax,38
mov ebx,105*65536+176
mov ecx,25*65536+25
xor edx,edx
int 0x40
mov ecx,90*65536+90
int 0x40
mov ebx,105*65536+105
mov ecx,25*65536+90
int 0x40
mov ebx,176*65536+176
int 0x40
ret
draw_ramka2: ;метод прямых -
рисование рамки двумя прямыми линиями
mov eax,38
mov ebx,106*65536+175
mov ecx,26*65536+26 ;верхная
mov edx,0x00E0E0E0
int 0x40
add ebx,2
mov ecx,91*65536+91 ;нижная больше
верхней на пиксель
int 0x40
mov ebx,106*65536+106
mov ecx,26*65536+89 ;боковая левая
int 0x40
add ecx,1
mov ebx,177*65536+177 ;бокавая правая
int 0x40
ret
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
draw_window:
mov eax,12
xor ebx,ebx
add ebx,1 ;быстрее на скалярных процессорах
xor eax,eax
mov ebx,50*65536+480
mov ecx,30*65536+100
mov edx,0x00AABBCC
mov esi,0x805080DD
mov edi,0x005080DD
int 0x40
mov eax,8
mov ebx,(480-17)*65536+12
mov ecx,4*65536+12
xor edx,edx
add edx,1
mov esi,0x6688DD
int 0x40
shr eax,1 ;то же но сдвиги быстрее т.е.
получается 8 разделить на 2 (на 386 процессорах)
;mov eax,4 ;если предполагается использовать
скалярный процессор, то можно использовать данню инструкцию
mov ebx,(480-13)*65536+7
mov ecx,0x00DDEEFF
mov edx,0x2B
xor esi,esi
add esi,1
int 0x40
mov ebx,(400-30)*65536+7
mov edx,0x2D
int 0x40
mov ebx,8*65536+8
mov ecx,0x10DDEEFF
mov edx,hed
mov esi,i_end - hed
int 0x40
mov ebx,205*65536+25
xor ecx,ecx
mov edx,mess1
;mov esi,mess1_end - mess1
int 0x40
loop_s:
add ebx,10
add edx,i_end - hed
int 0x40
cmp ebx,205*65536+25+60
jb loop_s
call @PROV_BIT_AND_DRAW ;
проверка битов и перерисовка рамки в соответствии с битовой матрицей
mov eax,12
mov ebx,2
int 0x40
ret
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
@PROV_BIT_AND_DRAW: ; проверка
битовой матрицы и перерисовка в соответствии с ней объектов т.е. прямых
mov al,[draw_bt_ramka] ; загрузим значение переменой
в которой наше битовое состояние нарисованных прямых т.к. с регистром
обращаться более эффективнее
test al,1 ; т.к. возможны варианты
0,1,2,3,4,5,6,7 и того 8 значений, хотя тут мы проверяем биты
jnz draw_@r ;отрисовка первой рамки переход
если 0 бит =1
@_bt1: test
al,2
jnz draw_@r1 ;отрисовка черного рамки переход
если 1 бит =1
@_bt2: test
al,4
jnz draw_@r2 ;отрисовка белого рамки переход
если 3 бит =1
ret
draw_@r:
push
eax ;сохраним нашу
битовую матрицу состояния объектов
call draw_ramka ; рисуем
черную и белую рамку
pop
eax ;восстановим
битовую матрицу состояния объектов
jmp @_bt1
draw_@r1:
push eax
call draw_ramka1 ;Рисуем
только черную рамку для демонстрации
pop eax
jmp @_bt2
draw_@r2:
call draw_ramka2 ; рисуем белую рамку
ret
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
;DATA данные
draw_bt_ramka db 0 ; т.к. переменная в которой храниться битовое
состояние объектов
mess1 db 'Данная программа демонстрирует метод построе-'
mess2 db 'ния рамки методом прямых. Для демонстрации '
mess3 db 'возможностей вам необходимо нажать D or d и '
mess4 db 'цифры 1 и 2 произойдет прорисовка рамки и '
mess5 db 'демонстрация построения. C or с - очищает '
mess6 db 'битовую матрицу состояния, можно убедиться, '
mess7 db 'перекрыв это окно другим
окном.
'
hed db 'Less1 - пример рисования рамки методом прямых'
i_end: