Очередной интересный визуальный эффект для ваших программ - бегущая строка. Обычно подобный эффект используется в различных кейгенах, но вполне может пригодиться и для более мирных приложений. В качестве основы взята ассемблерная библиотека
Scroller lib 1.01 от
ReWolf / HTBTeam. Изначально она предназначалась для MASM, так что пришлось ее портировать под FASM. В процессе переноса библиотека была немного переработана и дополнена, а также упрощена ее структура и подключение к проектам. Сперва в сегменте данных определяются вспомогательные константы и структура для создания скроллера.
; Константы, определяющие режим скроллера
SCROLL_START = 0 ; Скроллер запущен
SCROLL_PAUSE = 1 ; Скроллер на паузе
SCROLL_STOP = 2 ; Скроллер полностью остановлен
; Структура для скроллера
struct SCROLLSTRUCT
; Значения заполняются пользователем
hwDlg dd ? ; Хэндл окна для скроллера
hFont dd ? ; Шрифт скроллера
ddWidth dd ? ; Ширина скроллера
X dd ? ; Координата X
Y dd ? ; Координата Y
lpstrText dd ? ; Строка скроллера ASCIIZ
ddTxtColor dd ? ; Цвет текста скроллера
ddBkgColor dd ? ; Цвет фона скроллера
ddSpeed dd ? ; Скорость движения
dbMode db ? ; Режим скроллера
; Внутренние переменные
ddROP dd ?
hBkgDC dd ?
hTxtBmp dd ?
hTmpBmp dd ?
hBakTmpBmp dd ?
hInDC dd ?
ddHeight dd ?
ddPos dd ?
ddTxtWidth dd ?
ends
В структуре два типа переменных - пользовательские и служебные. Пользовательские предназначены для задания координат, цвета и других параметров скроллера, они все обязательны для заполнения. Служебные используются процедурой скроллера и не должны изменяться пользователем.
После инициализации переменных вызывается основная функция скроллера. Обратите внимание, что скроллер должен быть привязан только к существующему и видимому окну, поэтому в функции выполняется проверка существования родительского окна, а само окно принудительно делается видимым. Принцип работы скроллера в том, что из окна считывается фрагмент заданного размера, текст скроллера преобразуется в картинку, а затем в цикле по ней передвигается "кадр" и накладывается на фрагмент из окна.
;-----------------------------------------------------------------------
; Процедура инициализации и запуска скроллера
; by ManHunter / PCL
; http://www.manhunter.ru
;
; Based on Scroller lib v1.01 by ReWolf / HTBTeam
; http://rewolf.prv.pl
;
; Параметры вызова:
; lpScrollData - указатель на заполненную структуру SCROLLSTRUCT
;-----------------------------------------------------------------------
proc Scroller lpScrollData:DWORD
; Локальные переменные
local sz_x :DWORD
local sz_y :DWORD
local ddLen :DWORD
local hBkgBrush :DWORD
local hBkgPen :DWORD
; Локальная проекция структуры
virtual at 0
scroll SCROLLSTRUCT
end virtual
; Сохранить все регистры
pusha
; Указатель на структуру SCROLLSTRUCT
mov edi,[lpScrollData]
; Родительское окно существует?
invoke IsWindow,[edi+scroll.hwDlg]
or eax,eax
jnz @f
; Восстановить все регистры
popa
; EAX = 0, функция завершилась с ошибкой
xor eax,eax
ret
@@:
; Сделать окно видимым и обновить его
invoke ShowWindow,[edi+scroll.hwDlg],SW_SHOW
invoke UpdateWindow,[edi+scroll.hwDlg]
; Получить контекст устройства окна
invoke GetDC,[edi+scroll.hwDlg]
mov [edi+scroll.hBkgDC],eax
; Режим наложения AND
mov [edi+scroll.ddROP],SRCAND
; Стартовая позиция скроллера = 0
mov [edi+scroll.ddPos],0
invoke CreateCompatibleDC,[edi+scroll.hBkgDC]
mov [edi+scroll.hInDC],eax
; Установить шрифт скроллера
invoke SelectObject,[edi+scroll.hInDC],[edi+scroll.hFont]
; Получить длину строки скроллера
invoke lstrlen,[edi+scroll.lpstrText]
mov [ddLen],eax
lea eax,[sz_x]
invoke GetTextExtentPoint32,[edi+scroll.hInDC],[edi+scroll.lpstrText],\
[ddLen],eax
push [sz_y]
pop [edi+scroll.ddHeight]
push [sz_x]
pop [edi+scroll.ddTxtWidth]
mov eax,[edi+scroll.ddWidth]
shl eax,1
add eax,[edi+scroll.ddTxtWidth]
invoke CreateCompatibleBitmap,[edi+scroll.hBkgDC],eax,\
[edi+scroll.ddHeight]
mov [edi+scroll.hTxtBmp],eax
invoke SelectObject,[edi+scroll.hInDC],[edi+scroll.hTxtBmp]
invoke SetBkMode,[edi+scroll.hInDC],OPAQUE; TRANSPARENT;
invoke SetTextColor,[edi+scroll.hInDC],[edi+scroll.ddTxtColor]
invoke CreateSolidBrush,[edi+scroll.ddBkgColor]
mov [hBkgBrush],eax
invoke SelectObject,[edi+scroll.hInDC],eax
invoke CreatePen,PS_SOLID,1,[edi+scroll.ddBkgColor]
mov [hBkgPen],eax
invoke SelectObject,[edi+scroll.hInDC],eax
mov eax,[edi+scroll.ddWidth]
shl eax,1
add eax,[edi+scroll.ddTxtWidth]
invoke Rectangle,[edi+scroll.hInDC],0,0,eax,[edi+scroll.ddHeight]
invoke TextOut,[edi+scroll.hInDC],[edi+scroll.ddWidth],0,\
[edi+scroll.lpstrText],[ddLen]
invoke SelectObject,[edi+scroll.hInDC],[hBkgPen]
invoke SelectObject,[edi+scroll.hInDC],[hBkgBrush]
invoke DeleteObject,[hBkgPen]
invoke DeleteObject,[hBkgBrush]
invoke CreateCompatibleBitmap,[edi+scroll.hBkgDC],\
[edi+scroll.ddWidth],[edi+scroll.ddHeight]
mov [edi+scroll.hBakTmpBmp],eax
invoke SelectObject,[edi+scroll.hInDC],eax
invoke BitBlt,[edi+scroll.hInDC],0,0,[edi+scroll.ddWidth],\
[edi+scroll.ddHeight],[edi+scroll.hBkgDC],[edi+scroll.X],\
[edi+scroll.Y],SRCCOPY
invoke SelectObject,[edi+scroll.hInDC],[edi+scroll.hBakTmpBmp]
invoke CreateCompatibleBitmap,[edi+scroll.hBkgDC],\
[edi+scroll.ddWidth],[edi+scroll.ddHeight]
mov [edi+scroll.hTmpBmp],eax
; Запустить основную процедуру скроллера
xor eax,eax
push eax
invoke CreateThread,NULL,NULL,scroll_proc,[lpScrollData],NULL,esp
pop eax
; Обновить родительское окно
invoke UpdateWindow,[edi+scroll.hwDlg]
; Восстановить все регистры
popa
; EAX = 1, функция выполнена успешно
xor eax,eax
inc eax
ret
endp
Вспомогательная функция скроллера запускается в отдельном потоке, управляется только через поле dbMode структуры SCROLLSTRUCT соответствующего скроллера. Пример смотрите в прилагаемом исходнике. При полной остановке скроллера поток завершается и скроллер становится недоступным для управления.
;-----------------------------------------------------------------------
; Вспомогательная функция скроллера, напрямую не вызывается
;-----------------------------------------------------------------------
proc scroll_proc lpScrollData:DWORD
; Локальная проекция структуры
virtual at 0
pscroll SCROLLSTRUCT
end virtual
; Указатель на структуру SCROLLSTRUCT
mov edi,[lpScrollData]
.scroll_loop:
; Проверить режим скроллера
cmp [edi+pscroll.dbMode],SCROLL_STOP
; Выход
je .scroll_exit
; Если скроллер только создан, то отрисовать его
cmp [edi+pscroll.ddPos],1
jbe @f
; Выдержать паузу согласно заданной скорости
invoke Sleep,[edi+pscroll.ddSpeed]
; Скроллер приостановлен?
cmp [edi+pscroll.dbMode],SCROLL_PAUSE
je .scroll_loop
@@:
mov ebx,[edi+pscroll.ddTxtWidth]
add ebx,[edi+pscroll.ddWidth]
cmp ebx,[edi+pscroll.ddPos]
jne @f
mov [edi+pscroll.ddPos],0
@@:
invoke CreateCompatibleDC,[edi+pscroll.hInDC]
mov esi,eax
invoke SelectObject,eax,[edi+pscroll.hBakTmpBmp]
invoke SelectObject,[edi+pscroll.hInDC],[edi+pscroll.hTmpBmp]
invoke BitBlt,[edi+pscroll.hInDC],0,0,[edi+pscroll.ddWidth],\
[edi+pscroll.ddHeight],esi,0,0,SRCCOPY
invoke SelectObject,esi,[edi+pscroll.hTxtBmp]
invoke BitBlt,[edi+pscroll.hInDC],0,0,[edi+pscroll.ddWidth],\
[edi+pscroll.ddHeight],esi,[edi+pscroll.ddPos],\
0,[edi+pscroll.ddROP]
invoke BitBlt,[edi+pscroll.hBkgDC],[edi+pscroll.X],\
[edi+pscroll.Y],[edi+pscroll.ddWidth],[edi+pscroll.ddHeight],\
[edi+pscroll.hInDC],0,0,SRCCOPY
invoke DeleteDC,esi
inc [edi+pscroll.ddPos]
jmp .scroll_loop
.scroll_exit:
ret
endp
В приложении пример программы с двумя независимыми скроллерами, по нажатию кнопки Pause они останавливаются, при повторном нажатии запускаются снова.