ЭФФЕКТИНОЕ ПРОГРАММИРОВАНИЕ В KOLIBRI OS
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>><<<<<<<<<<<<<<<<<<<<<<<<<<<<<

Автор - Теплов Алексей. icq:267994076 e-mail: tay-ugur@chel.surnet.ru
INTRO - ВСТУПЛЕНИЕ
Написать сборник статей на тему Эффективное программирование в КОЛИБРИ меня побудило то, что
большинство рассмотренных мной программ не всегда эффективно использовали возможности системы.
В большинстве своем, задача решалась по пути наименьшего сопротивления, что безусловно не
может радовать. Многие методы в своей структуре достаточно ошибочны или неверны, или, в лучшем
случае отражают только одну сторону многогранной действительности...

Я далеко не самый лучший программист, наверное я как и все - просто ищет истину, а она где-то рядом
======================================
Сегодня я постараюсь описать как можно вывести рамку в рабочее поле программы т.е. на канву окна приложения. Зачем это нужно? Так вот, очень часто я встречаю в коде конструкции, кода когда для того что бы изменить или перерисовать кнопку или еще что на канве, прибегают к такому коду: Изменяют значение битового состояния переменной, ответственной за то или иное состояние объекта и без зазрения совести перерисовывают полностью окно. Из-за такого кода окно вынужденно часто моргать, что очень раздражает!
Сегодня я расскажу, как можно грамотно выйти из данной ситуации, с наименьшими потерями. Решение напрашивается само по себе - это при изменении объекта перерисовать его индивидуально!
Рассмотрим несколько вариантов того как это можно сделать
НЕ ОПТИМАЛЬНЫЙ ВАРИАНТ:
Рисование закрашеного прямоугольника системой, происходит, как рисование набора линий. По этому данный вариант будет выполняться медленее чем индивидуальная прорисовка рамки с помощью линий!
1) Нарисовать черный прямоугольник, затем прямоугольник светлый(цвет фона) меньшего размера, получится
рамка черного цвета или же того который вы зададите. Вот собственно и идея. Рассмотрим вопрос, с реализацией перерисовки прямоугольников, при изменении положения окна.
1) При перемещении, активного окна, по области рабочего стола перерисовывать как бы и не за чем, т.к. нет перекрытия. Однако, у нас есть одно системное событие, которое нас информирует об изменении положения окна и тут мы можем только постараться перерисовать только то, что  необходимо. У нас на канве окна нарисованы 2 объекта. 1 - это рамка, состоящая из 2-х  прямоугольников, которые рисуются последовательно, сначала рисуется черный прямоугольник, затем рисуется прямоугольник цвета фона в области черного прямоугольника, тем самым достигается получение рамки.
Элементы управления в данной программе реализованы так:
 Толщину рамки можно варьировать т.к. она задается с помощью размеров прямоугольника имеющего цвет фон, а он располагается внутри черного прямоугольника. Объект № 2 это, по сути, те же 2 прямоугольника, но для наглядности прорисовкой их можно управлять, с помощью клавиш, т.е. при нажатии 1 происходит прорисовка черного прямоугольника, а при нажатии 2 происходит прорисовка фонового прямоугольника и тем самым получается рамка.
       В данной программе рассмотрен эффективный метод прорисовки на канве окна рамок с помощью прямоугольников, причем только в тех случаях в каких это не обходимо. Для этого используется  переменная, в которой храниться битовое состояние объектов, т.е. при происхождении события в данной переменной устанавливается бит в соответствии с произошедшим событиям. В данном случае используется переменная draw_bt_ramka, в которой и храниться состояние объектов. Рассмотрим, за что отвечает каждый бит в данной переменной.
Теперь разберем подробнее логику, когда можно прорисовывать, а когда нет.
Рассмотрим возможные состояния нарисованных прямоугольников.   
Ничего не нарисованно
Нарисована первая рамка
1-2
1-3

1-5
Разработаем метод управления данными событиями:
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: