Советы по Delphi

         

Как создать disable'ный битмап из обычного (emboss etc)?


Nomadic советует:

CreateMappedBitmap() :-)

Один из паpаметpов yказатель на COLORMAP, в нем для 16 основных цветов делаешь пеpекодиpовкy, цвета подбеpешь сам из пpинципа: все самые яpкие -> в GetSysColor( COLOR_3DLIGHT ); самые темные -> GetSysColor( COLOR_3DSHADOW ); нейтpальные, котpые бyдyт пpозpачные -> GetSysColor( COLOR_3DFACE ); Так на самом деле вот как делается данная задача:

    procedure Tform1.aaa(bmpFrom,bmpTo:Tbitmap);
var
TmpImage,Monobmp:TBitmap;
IRect:TRect;
begin
MonoBmp := TBitmap.Create;
TmpImage:=Tbitmap.Create;
TmpImage.Width := bmpFrom.Width;
TmpImage.Height := bmpFrom.Height;
IRect := Rect(0, 0, bmpFrom.Width, bmpFrom.Height);


TmpImage.Canvas.Brush.Color := clBtnFace;
try
with
MonoBmp do
begin

Assign(bmpFrom);
Canvas.Brush.Color := clBlack;
if Monochrome then
begin

Canvas.Font.Color := clWhite;
Monochrome := False;
Canvas.Brush.Color := clWhite;
end;
Monochrome := True;
end;
with TmpImage.Canvas do
begin

Brush.Color := clBtnFace;
FillRect(IRect);
Brush.Color := clBlack;
Font.Color := clWhite;
CopyMode := MergePaint;
Draw(IRect.Left + 1, IRect.Top + 1, MonoBmp);
CopyMode := SrcAnd;
Draw(IRect.Left, IRect.Top, MonoBmp);
Brush.Color := clBtnShadow;
Font.Color := clBlack;
CopyMode := SrcPaint;
Draw(IRect.Left, IRect.Top, MonoBmp);
CopyMode := SrcCopy;
bmpTo.assign(TmpImage);
TmpImage.free;
end;
finally
MonoBmp.Free;
end;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
aaa(image1.picture.bitmap,image2.picture.bitmap);
Image2.invalidate;
end;

Писал это не я. Это написал сам Борланд (некузявно было бы взглянуть на класс TButtonGlyph. Как раз из него я это и выдернул).Hу а если уже совсем хорошо разобраться, то можно заметить функцию ImageList_DrawEx, в которой можно на 25 и 50 процентов уменьшить яркость (но визуально это очень плохо воспринимается). Соответственно параметры ILD_BLEND25, ILD_BLEND50, ILD_BLEND-A-MED. Естественно, что последний абзац работает только с тройкой.

Denis Tanayeff

Это кусочек из рабочей проги на Си, Вроде все лишнее я убрал. #define CO_GRAY 0x00C0C0C0L hMemDC = CreateCompatibleDC(hDC); hOldBitmap = SelectObject(hMemDC, hBits); // hBits это собственно картинка, которую надо "засерить" GetObject(hBits, sizeof(Bitmap), (LPSTR) &Bitmap); if ( GetState(BS_DISABLED) ) // Blt disabled { hOldBrush = SelectObject(hDC, CreateSolidBrush(CO_GRAY));//CO_GRAY PatBlt(hDC, BD_BORDERWIDTH, BD_BORDERWIDTH, Bitmap.bmWidth, Bitmap.bmHeight, PATCOPY); DeleteObject(SelectObject(hDC, hOldBrush)); lbLogBrush.lbStyle = BS_PATTERN; lbLogBrush.lbHatch =(int)LoadBitmap(hInsts, MAKEINTRESOURCE(BT_DISABLEBITS)); hOldBrush = SelectObject(hDC, CreateBrushIndirect(&lbLogBrush)); BitBlt(hDC, BD_BORDERWIDTH, BD_BORDERWIDTH, Bitmap.bmWidth, Bitmap.bmHeight, hMemDC, 0, 0, 0x00A803A9UL); // DPSoa DeleteObject(SelectObject(hDC, hOldBrush)); DeleteObject((HGDIOBJ)lbLogBrush.lbHatch); } [001129]



Как создать не VCL дисплей для отображения текста и изображений?


Своим опытом делится Олег Кулабухов:

    procedure TForm1.Button1Click(Sender: TObject);
var
dc : hdc;
MemDc : hdc;
MemBitmap : hBitmap;
OldMemBitmap : hBitmap;
begin

{Get the handle to the screen's dc}
dc := GetDc(0);

{Create and retrieve a handle to a memory dc based on the screen}
MemDc := CreateCompatibleDc(dc);

{Create a bitmap that is compatible with the display.}
{Note: if you pass "MemDc" to CreateCompatibleBitmap()}
{instead of "dc", you will get a monochrome bitmap!}
MemBitmap := CreateCompatibleBitmap(dc, 100, 100);

{Release the screen dc}
ReleaseDc(0, dc);

{Select the bitmap surface into the MemDc}
{remembering the default bitmap}
OldMemBitmap := SelectObject(MemDc, MemBitmap);

{Draw on the MemoryDc}
PatBlt(MemDc, 0, 0, 100, 100, WHITENESS);
Ellipse(MemDc, 0, 0, 100, 100);

{Copy the MemDc to the Form Canvas}
BitBlt(Form1.Canvas.Handle,
100, 100,
100, 100,
MemDc,
0, 0,
SRCCOPY);

{Select the default bitmap back into the memory dc}
SelectObject(MemDc, OldMemBitmap);

{Note: You can now use the memory bitmap handle with}
{functions such as GetDiBits()}

{Delete the Memory Bitmap}
DeleteObject(MemBitmap);

{Delete the MemoryDc}
DeleteDc(MemDc);

end;

[001900]



Как установить прозрачность фона текста?


Своим опытом делится Олег Кулабухов:

Используем SetBkMode()

    procedure TForm1.Button1Click(Sender: TObject);
var
OldBkMode : integer;
begin
with
Form1.Canvas do begin
Brush.Color := clRed;
FillRect(Rect(0, 0, 100, 100));
Brush.Color := clBlue;
TextOut(10, 20, 'Not Transparent!');
OldBkMode := SetBkMode(Handle, TRANSPARENT);
TextOut(10, 50, 'Transparent!');
SetBkMode(Handle, OldBkMode);
end;
end;

[001930]



Как вывести на Canvas надпись под углом?


Nomadic советует:

Вот, взгляни.

    ...

function CreateRotatedFont(F : TFont; Angle : Integer) : hFont;
{-create a rotated font based on the font object F} var
LF : TLogFont; begin
FillChar(LF, SizeOf(LF), #0); with LF do begin lfHeight           := F.Height; lfWidth            := 0; lfEscapement       := Angle*10; lfOrientation      := 0; if fsBold in F.Style then lfWeight         := FW_BOLD else lfWeight         := FW_NORMAL; lfItalic           := Byte(fsItalic in F.Style); lfUnderline        := Byte(fsUnderline in F.Style); lfStrikeOut        := Byte(fsStrikeOut in F.Style); lfCharSet          := DEFAULT_CHARSET; StrPCopy(lfFaceName, F.Name); lfQuality         := DEFAULT_QUALITY; {everything else as default} lfOutPrecision     := OUT_DEFAULT_PRECIS; lfClipPrecision    := CLIP_DEFAULT_PRECIS; case F.Pitch of fpVariable : lfPitchAndFamily := VARIABLE_PITCH; fpFixed    : lfPitchAndFamily := FIXED_PITCH; else lfPitchAndFamily := DEFAULT_PITCH; end; end; Result := CreateFontIndirect(LF); end;

...

{create the rotated font} if FontAngle <&tg; 0 then Canvas.Font.Handle := CreateRotatedFont(Font, FontAngle); ...

Вращаются только векторные шрифты. [001614]



Как вывести на экран текст с 'красивым' обрезанием по длине (если текст не помещается на экране)?


Nomadic советует:

Используй вызов DrawTextEx, установив в параметре dwDTFormat значение DT_PATH_ELLIPSIS. [001230]



Как записать содержимое окна OpenGL в 'bmp' файл?


Nomadic советует:

Вот что попробовал - вроде получилось:

    bt := TBitmap.Create;
bt.Width := gr.Width;
bt.Height := gr.Height;
bt.Canvas.CopyRect(ClientRect, gr.Canvas, gr.ClientRect);
bt.SaveToFile('e:\bt.bmp');
bt.Free;

(gr - объект, в канве которого я рисую с помощью OpenGL) [001108]



Код создания палитры


    var
Form1: TForm1; blueVal : Byte; BluePalette : HPalette; implementation

{$R *.DFM}

procedure TForm1.FormCreate(Sender: TObject);
var
LogicalPalette: PLogPalette; ColorIndex : LongInt; begin
GetMem(LogicalPalette, (SizeOf(TLogPalette) + SizeOf(TPaletteEntry)*256));
GetSystemPaletteEntries(Canvas.Handle, 0, 256, LogicalPalette^.palPalEntry[0]); with LogicalPalette^ do
begin
palVersion := $300; palNumEntries := 256; {$R-} for ColorIndex := 10 to 245 do with palPalEntry[ColorIndex] do begin peRed := 0; peGreen := 0; peBlue := 255 - (ColorIndex-10); peFlags := PC_NOCOLLAPSE; end; end; {$R+} BluePalette := CreatePalette(LogicalPalette^);
FreeMem(LogicalPalette, (SizeOf(TLogPalette) + SizeOf(TPaletteEntry)*256));
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
DeleteObject(BluePalette); end;

procedure TForm1.FormMouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer); var
OldPal : HPALETTE; begin
OldPal := SelectPalette(Canvas.Handle, BluePalette, False); RealizePalette(Canvas.Handle); canvas.pen.color := $02000000 or (BlueVal * $00010000); canvas.pen.width := 10; canvas.moveto(0, 0); canvas.lineto(X,Y); SelectPalette(Canvas.Handle, OldPal, False); Inc(BlueVal);
If BlueVal > 255 Then BlueVal := 0; end;

[000654]



Компонент для отрисовки линий


Вот компонент, инкапсулирующий функции рисования линий. Он может рисовать горизонтальные, вертикальные и диагональные линии. Вы можете добавить необходимые вам события в секцию *published*.

    unit Lines;  {от Bill Murto, CIS 73730,2505}
interface
uses
SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls, Forms;
type TLineOrigin = (loTopLeft, loTopRight); TLine = class(TGraphicControl) private { Private declarations } fOrigin: TLineOrigin; fPen: TPen; procedure SetOrigin(Value: TLineOrigin); procedure SetPen(Value: TPen); protected { Protected declarations } procedure Paint; override; public { Public declarations } constructor Create(AOwner: TComponent); override; destructor Destroy; override; published { Published declarations } property Origin: TLineOrigin read fOrigin write SetOrigin default loTopLeft; property Pen: TPen read fPen write SetPen; property Height default 33; property Width default 33; procedure StyleChanged(Sender: TObject); end;
procedure Register;
implementation
procedure Register
; begin RegisterComponents('Samples', [TLine]); end;
procedure TLine.SetOrigin(Value: TLineOrigin); begin if fOrigin <> Value then begin fOrigin := Value; Invalidate; end; end;
procedure TLine.SetPen(Value: TPen); begin fPen.Assign(Value); end;
procedure TLine.StyleChanged(Sender: TObject); begin Invalidate; end;
constructor TLine.Create(AOwner: TComponent); begin inherited Create(AOwner); Height := 33; Width := 33; fPen := TPen.Create; fPen.OnChange := StyleChanged; if csOpaque in ControlStyle then ControlStyle := ControlStyle - [csOpaque]; end;
procedure TLine.Paint; begin with Canvas do begin Pen := fPen; if (Width <= Pen.Width) or (Height <= Pen.Width) then begin if Width <= Pen.Width then begin MoveTo(0, 0); LineTo(0, Height); end; if Height <= Pen.Width then begin MoveTo(0, 0); LineTo(Width, 0); end; end else case fOrigin of loTopLeft: begin MoveTo(0, 0); LineTo(Width, Height); end; loTopRight: begin MoveTo(Width, 0); LineTo(0, Height); end; end; end; end;
destructor TLine.Destroy; begin fPen.Free; inherited Destroy; end;
end.

- William E Murto [000784]



Компонент для работы с FLIC-анимацией


Новостная группа: comp.lang.pascal.delphi.components
Тема: Компонент для работы с FLIC-анимацией: отправной пункт.
От: Paul Kuczora <paul@kuczora.demon.co.uk>
Дата: Чет, 03 Авг 95 16:51:19 GMT

В качество ответа на целый круг вопросов, я включил в свой ответ два файла: aaplay1.inc - include-файл с интерфейсом для библиотеки aaplay.dll aaplay1.pas - сырой скелет компонента для проигрывания FLIC-анимации Я разработал это глядя на другой компонент (это был полнофункциональный плейер, работающий как форма), и вынужден был сохранить некоторые вещи неприкосновенными (попробуй тут сделай иначе :-)

Для работы вам понадобится библиотека aaplay.dll от Autodesk, которую вы можете найти на множестве мест (например, я так предполагаю, на Encarta CD). Для полного счастья вы можете обратиться к файлу помощи для Animation Player for Windows, который, не удивляйтесь, содержит справку для этой DLL - на первой странице найдите в ее самой нижней части указание на эту библиотеку, и перед вами предстанет полная справка по вызовам этой самой DLL.

Надеюсь что помог вам. . .

ВНИМАНИЕ! Это мой первый код, написанный для Windows (спасибо Delphi), поэтому он рекомендуется только для ознакомления.

    { ============================================================================ }

{ aaplay1.inc }
{ (c) P W Kuczora }
{ 17-го апреля 1995 }

{ Заголовочный файл, содержащий константы и определения типов для aaplay1.pas }

const
NULL = 0; NULLPTR = ^0;
{
Доступные Флаги wMode: integer; Используются в aaLoad, aaReLoad Первые восемь бит wMode используются в aa_flags. }
AA_MEMORYLOAD = $1;        { Загрузка в память } AA_HIDEWINDOW = $2;        { Скрывать окно анимации } AA_NOPALETTE = $4 ;        { Анимация без палитры } AA_RESERVEPALETTE = $8;    { Резервировать при старте всю палитру } AA_LOOPFRAME = $10;        { Циклическая загрузка кадров } AA_FULLSCREEN = $20;       { Использовать полноэкранный режим воспроизведения } AA_STOPNOTIFY = $40;       { Исключать любые уведомляющие сообщения } AA_STOPSTATUS = $80;       { Исключать сообщения об изменении статуса } AA_NOFAIL = $100;          { Уменьшение типа нагрузки при ошибке } AA_DONTPAINT = $200;       { Не делать paByVal-анимацию при загрузке } AA_BUILDSCRIPT = $400;     { lpzFileName - скрипт, не имя } AA_ALLMODES = $FF;
{
Доступные флаги для режимов звука - wMode: integer; Используются в aaSound }

AA_SNDFREEZE = $1;         { Заморозка кадров при проигрывании звуков } AA_SNDDEVICEID = $100;     { ID устройства, не имя } AA_SNDBUILDALIAS = $200;   { создавать псевдоним звукового устройства } { aaNotify позволяет извещать приложение о проигрывании определенных кадров.
lPosition - позиция, на которой должно происходить уведомление.
wParam для этого сообщения - hAa, а lParam копируется из этого вызова.
При установке сообщения возвращается TRUE.
Следующее значение определяет необходимость завершения цикла анимации по окончании проигрывания звука. Если звук отсутствует, анимация зацикливается навсегда. }
AA_LOOPSOUND = $FFFF;
{
Автоматическое уведомление посылается при перезагрузке в скрипте анимации.
lParam для этого сообщения определен ниже }
AA_ANIMATIONLOADED = 0;
{
Типы параметров Используется с aaGetParm и aaSetParm. }
AA_STATUS = 1;                      { Получить текущий статус } AA_FILETYPE = 2;                    { Получить тип анимации на диске } AA_MODE = 3;                        { Получить/установить флаги анимации } AA_WINDOW = 4;                      { Установить/получить окно анимации } AA_SPEED = 5;                       { Установить/получить текущую скорость } AA_DESIGNSPEED = 6;                 { Получить скорость на этапе дизайна } AA_FRAMES = 7;                      { Получить число кадров } AA_POSITION = 8;                    { Установить/получить позицию текущего кадра } AA_LOOPS = 9;                       { Установить/получить число циклов } AA_X = 10;                          { Установить/получить позицию выводимого окна } AA_Y = 11;                          { Установить/получить позицию выводимого окна } AA_CX = 12;                         { Установить/получить размеры выводимого окна } AA_CY = 13;                         { Установить/получить размеры выводимого окна } AA_ORGX = 14;                       { Установить/получить начало выводимого окна } AA_ORGY = 15;                       { Установить/получить начало выводимого окна } AA_WIDTH = 16;                      { Получить ширину анимации } AA_HEIGHT = 17;                     { Получить высоту анимации } AA_RPTSOUND = 18;                   { Установить/получить повторения звуков } AA_PAUSE = 19;                      { Установить/получить время паузы } AA_DELAYSND = 20;                   { Установить/получить время задержки звука } AA_TRANSIN = 21;                    { Установить/получить тип входного перехода } AA_TRANSOUT = 22;                   { Установить/получить тип выходного перехода } AA_TIMEIN = 23;                     { Установить/получить время входного перехода } AA_TIMEOUT = 24;                    { Установить/получить время выходного перехода } AA_CALLBACK = 25;                   { Установить/получить окно обратного вызова } AA_ANIMWND = 26;                    { Получить дескриптор окна анимации } AA_MODFLAG = 100;                   { Установить/получить флаг изменения скрипта } AA_SCRIPTNAME = 101;                { Установить/получить имя скрипта } AA_ANIMATION = 102;                 { Получить/установить скрипт анимации } AA_ANIMATIONCOUNT = 103;            { Получить счетчик скрипта анимации } AA_SCRIPTCONTENTS = 104;            { Получить содержание скрипта } AA_LASTERROR = 1001;                { Получить код последней ошибки } AA_LASTERRORMESSAGE = 1002;         { Получить/установить сообщение о последней ошибке }
{
Типы параметров Используется с aaSetParmIndirect }
AA_SETMODE = $1;                  { Получить/установить флаги анимации } AA_SETWINDOW = $2;                { Установить/получить окно анимации } AA_SETSPEED = $4;                 { Установить/получить текущую скорость } AA_SETPOSITION = $8;              { Установить/получить позицию текущего кадра } AA_SETLOOPS = $10;                { Установить/получить число циклов } AA_SETX = $20;                    { Установить/получить левую координату выводимого окна } AA_SETY = $40;                    { Установить/получить левую координату выводимого окна } AA_SETCX = $80;                   { Установить/получить верхнюю координату выводимого окна } AA_SETCY = $100;                  { Установить/получить верхнюю координату выводимого окна } AA_SETORGX = $200;                { Установить/получить ширину выводимого окна } AA_SETORGY = $400;                { Установить/получить ширину выводимого окна } AA_SETRPTSOUND = $800;            { Установить/получить повторения звуков } AA_SETPAUSE = $1000;              { Установить/получить время паузы } AA_SETDELAYSND = $2000;           { Установить/получить время задержки звука } AA_SETTRANSIN = $4000;            { Установить/получить тип входного перехода } AA_SETTRANSOUT = $8000;           { Установить/получить тип выходного перехода } AA_SETTIMEIN = $10000;            { Установить/получить время входного перехода } AA_SETTIMEOUT = $20000;           { Установить/получить время выходного перехода } AA_SETCALLBACK = $40000;          { Установить/получить окно обратного вызова } AA_ALL = $FFFFFFFF;               { Получить/установить все параметры }
{
Значения статуса для анимации }
AA_STOPPED = 1;             { Загружена, но не воспроизводится } AA_QUEUED = 2;              { Анимация ожидает воспроизведение } AA_PLAYING = 3;             { Анимация воспроизводится } AA_PAUSED = 4;              { Анимация в режиме паузы } AA_DONE = 5;                { Анимация закончила воспроизведение } { и ожидает вызов aaStop }
{
Определения типов файла }
AA_FLI = $1;               { Формат Autodesk Animator Fli } AA_DIB = $2;               { Формат Windows DIB } AA_NUMTYPES = $2;          { Количество типов } AA_SCRIPT = $3;            { Скрипт без анимации }
{
Типы переходов }
AA_CUT = 0;                { Простая остановка одной и запуск другой } AA_FADEBLACK = $1;         { Уход/выход из черного } AA_FADEWHITE = $2;         { Уход/выход из белого }
{
Коды ошибок, возвращаемые aaGetParm(xxx, AA_LASTERROR) }
AA_ERR_NOERROR = 0;        {  Неизвестная ошибка  }
AA_ERR_NOMEMORY = $100;    {  256 - Ошибка нехватки памяти } AA_ERR_BADHANDLE = $101;   {  257 - Плохой дескриптор } AA_ERR_NOTIMERS = $102;    {  258 - Невозможно запустить таймер } AA_ERR_BADSOUND = $103;    {  259 - Плохое звуковое сопровождение } AA_ERR_NOSCRIPT = $104;    {  260 - Требуется скрипт } AA_ERR_WRITEERR = $105;    {  261 - Ошибка записи (для сценария) } AA_ERR_BADANIMATION = $106;  {  262 - Невозможно открыть анимацию }
AA_ERR_BADWINDOWHANDLE = $200;     {  512 - Плохой дескриптор окна } AA_ERR_WINDOWCREATE = $201;        {  513 - Невозможно создать окно } AA_ERR_DLGERROR = $202;            {  514 - Ошибка диалога }
AA_ERR_INVALIDSTATUS = $300;       {  768 - Неверный статус } AA_ERR_BADDIBFORMAT = $301;        {  769 - Плохой dib-файл } AA_ERR_BADFLIFORMAT = $302;        {  770 - Плохой fli-файл } AA_ERR_UNRECOGNIZEDFORMAT = $303;  {  771 - Нераспознанный формат } AA_ERR_NOSOUND = $304;             {  772 - Звук не поддерживается } AA_ERR_NOTVALIDFORSCRIPTS = $305;  {  773 - Неправильный сценарий } AA_ERR_INVALIDFILE = $306;         {  774 - Плохой дескриптор файла } AA_ERR_NOSCRIPTS = $307;           {  775 - Нет файлов-скриптов }
AA_ERR_SPEED = $400;            {  1024 - Неверная скорость  } AA_ERR_LOOPS = $401;            {  1025 - Неверные циклы } AA_ERR_RPTSOUND = $402;         {  1026 - Неверный повтор звука } AA_ERR_PAUSE = $403;            {  1027 - Неверная пауза } AA_ERR_TRANSIN = $404;          {  1028 - Неверный переход } AA_ERR_TIMEIN = $405;           {  1029 - Неверный переход } AA_ERR_TRANSOUT = $406;         {  1030 - Неверное время перехода } AA_ERR_TIMEOUT = $407;          {  1031 - Неверное время перехода } AA_ERR_DELAYSND = $408;         {  1032 - Неверная задержка звука } AA_ERR_INVALIDTYPE = $409;      {  1033 - Неверный тип параметра }
AA_ERR_DUPLICATENOTIFY = $500;     {  1280 - Дублирование уведомления }
AA_ERR_NOSWITCH = $600;          {  1536 - Отсутствие ключей в скрипте } AA_ERR_PARSELOOPS = $601;        {  1537 - Плохие циклы в скрипте } AA_ERR_PARSESPEED = $602;        {  1538 - Плохая скорость в скрипте } AA_ERR_BADRPTSOUND = $603;       {  1539 - Плохое повторение звука в скрипте } AA_ERR_PARSEPAUSE = $604;        {  1540 - Плохая пауза в скрипте } AA_ERR_PARSETRANS = $605;        {  1541 - Плохой переход в скрипте } AA_ERR_PARSEDELAYSND = $606;     {  1542 - Плохая задержка звука в скрипте } AA_ERR_TOOMANYLINKS = $607;      {  1543 - Слишком много ссылок }

{
dwFlags: integer; может быть любым из нижеперечисленных Используется в aaGetFile. }
AA_GETFILE_MUSTEXIST = $1; AA_GETFILE_NOSHOWSPEC = $2; AA_GETFILE_SAVE = $4; AA_GETFILE_OPEN = $8; AA_GETFILE_USEDIR = $10; AA_GETFILE_USEFILE = $20; AA_GETFILE_SOUND = $40; AA_GETFILE_SCRIPT = $80; AA_GETFILE_ANIMATION = $100;
{
wMode: integer; Значения Используется в aaSave }
AA_SAVE_IFMODIFIED = $1; AA_SAVE_AS = $2; AA_SAVE_CANCEL = $4;
{
Возможности Используется в aaGetCaps }
AA_CAP_TIMER = 1; AA_CAP_SOUND = 2; AA_CAP_SCRIPT = 3;
{
Статусные сообщения анимации Используйте RegisterWindowMessage для получения номеров реальных сообщений. }
AA_NOTIFY = 'AAPLAY Уведомление';   { Сообщение-уведомление } AA_STOP = 'AAPLAY Стоп';            { Стоп-сообщение }
{
Это посылается в первом слове lParam с сообщением AA_ERROR. Это указывает на возникшую ошибку }
AA_BADPLAY = 1;             { Ошибка при попытке воспроизведения } AA_BADNOTIFY = 2;           { Ошибка при попытке послать уведомление } AA_BADSCRIPT = 3;           { Ошибка в сценарии при попытке } { воспроизведения }

    { ========================================================================== }

unit aaplay1;
{ (c) P W Kuczora }
{ 27-го апреля 1995 }

interface

uses

SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls, Forms, Dialogs, MPlayer;
{$I AAPLAY1.INC}

const
AAPARMS_SIZE = 54;
type
AAHandle = word;                            { Дескриптор анимации } AASPEED = word;                             { Тип, содержащий скорость анимации } AATRN = word;                               { Тип для хранения перехода анимации } dword = longint;
{
Структура параметра должна быть использована для анимации. }
AAPARMS = record AA_STATUS           : byte;        {  Текущий статус анимации } AA_FILETYPE         : byte;        {  Тип файла на диске } AA_MODE             : byte;        {  Некоторые флаги } AA_bitpix           : byte;        {  бит на пиксел }
AA_HWnd             : HWnd;         {  Дескриптор окна для статусного сообщения } AA_X                : integer;      {  Левая координата выводимого окна } AA_Y                : integer;      {  Верхняя координата выводимого окна } AA_CX               : integer;      {  Ширина выводимого окна } AA_CY               : integer;      {  Высота выводимого окна } AA_ORGX             : integer;      {  PoByVal в показываемой анимации } AA_ORGY             : integer;      {  в верхнем левом углу }
AA_SPEED            : AASPEED;      {  Скорость анимации в миллисекундах на кадр } AA_DESIGNSPEED      : AASPEED;      {  Проектируемые миллисекунды на кадр }
AA_WIDTH            : word;         {  Ширина анимации в пикселах } AA_HEIGHT           : word;         {  Высота анимации в пикселах }
AA_FRAMES           : word;         {  Количество кадров в анимации }
AA_POSITION         : dword;        {  Текущая кадровая позиция } AA_LOOPS            : dword;        {  Конечная позиция анимации } AA_RPTSOUND         : word;         {  Количество повторов звука } AA_PAUSE            : word;         {  Количество миллисекунд замораживания кадра } AA_DELAYSND         : longint;      {  Задержка звука в миллисекундах } AA_TRANSIN          : byte;         {  Переход в начале анимации } AA_TRANSOUT         : byte;         {  Переход в конце анимации } AA_TIMEIN           : word;         {  Продолжительность входного перехода в миллисекундах } AA_TIMEOUT          : word;         {  Продолжительность выходного перехода в миллисекундах } AA_CALLBACK         : HWnd;         {  Сообщение окна обратного вызова } AA_ANIMWND          : Hwnd;         {  Дескриптор окна анимации } end;
AAPARMSPtr = ^AAPARMS;
{type}
TAAPlayer = class(TMediaPlayer) procedure OpenAA;
private { Private declarations } protected { Protected declarations } public { Public declarations } AAParameters: AAPARMS; FlicHandle: AAHandle; PlayWinHandle: THandle; StatusWinHandle: THandle; CallbackWinHandle: THandle; published { Published declarations } end;
procedure Register;

{ Внешние вызовы AAPLAY.DLL }

function aaOpen : boolean;
procedure aaClose;
function aaGetCaps(wType: word) : word;
function aaLoad(lpzFileName: PChar; WinHnd: HWnd; wMode: word;
x, y, wid, hght, orgx, orgy: integer): AAHandle; function aaReLoad(hAa: AAHandle; lpzFileName: PChar; wMode, wMask: word)
: boolean; function aaUnload(hAa: AAHandle): boolean;
function aaPlay(hAa: AAHandle) : boolean;
function aaNotify(hAa: AAHandle; lPosition, lParam: longint) : boolean;
function aaCancel(hAa: AAHandle; lLoPos, lHiPos: longint) : word;
function aaStop(hAa: AAHandle) : boolean;
function aaPause(hAa: AAHandle) : boolean;
function aaPrompt(hAa: AAHandle; lpName: PChar) : boolean;
function aaGetParm(hAa: AAHandle; wType: word) : longint;
function aaGetParmIndirect(hAa: AAHandle; lpAp: AAPARMSPtr; wSize: word)
: boolean; function aaSetParm(hAa: AAHandle; wType: word; wValue1, lValue2: longint)
: AAHandle; function aaSetParmIndirect(hAa: AAHandle; dwType: longint; lpAp: AAPARMSPtr;
wMask: word): boolean; function aaShow(hAa: AAHandle; bShow: boolean) : boolean;
function aaSound(hAa: AAHandle; device, ffile: PChar; wMode: word)
: boolean; function aaGetFile(dwFlags: word; lpszPath: PChar; wBufLen: word;
lpszDriver: PChar; wDrvLen: word) : integer; function aaSave(hAa: AAHandle; wMode: word) : integer;

implementation

{ =========================================================================== }

procedure Register;
begin
RegisterComponents('Samples', [TAAPlayer]); end;

{ --------------------------------------------------------------------------- }

procedure TAAPlayer.OpenAA;

var
FileSuffix, tempstr: string[12]; a,b: integer;
begin
{     tempstr := ExtractFilename(AAPlayer.Filename); }
{     a := StrPos(tempstr,'.');

if (a > 0) then begin b := a;
while (b <= StrLen(tmpstr)) do begin FileSuffix := FileSuffix + StrUpper(tempstr[b]); b := b+1; end;
if ((FileSuffix = '.FLC') or (FileSuffix = '.FLI')) then begin} {          AutoEnable := False;
EnabledButtons := [btRecord,btEject]; }           {end;

end; }
end;

{ =========================================================================== }
{ Внешние вызовы 'AAPLAY.DLL' }
{$F+}
{ =========================================================================== }

{ --------------------------------------------------------------------------- }
function aaOpen : boolean;
external 'AAPLAY';

{ --------------------------------------------------------------------------- }
procedure aaClose;
external 'AAPLAY';
{
'  AAOpen и AAClose в действительности не нужны, за исключением обработки
'  ошибки в Windows, которая предохраняет освобождение библиотек в процедуре
'  выхода Windows (Windows Exit Proc, WEP).
'
'  Поэтому мы используем AAClose для освобождения библиотек при закрытии
'  последней задачей AAPlay DLL.
}

{ --------------------------------------------------------------------------- }
function aaGetCaps(wType: word) : word;
external 'AAPLAY';
{
'  Получение возможностей
}

{ --------------------------------------------------------------------------- }
function aaLoad(lpzFileName: PChar; WinHnd: HWnd; wMode: word;
x, y, wid, hght, orgx, orgy: integer): AAHandle; external 'AAPLAY';
{
'  aaLoad загружает анимацию.
'
'  Имя файла в lpzFileName
'  и режим загрузки в wMode.
}

{ --------------------------------------------------------------------------- }
function aaReLoad(hAa: AAHandle; lpzFileName: PChar; wMode, wMask: word)
: boolean; external 'AAPLAY';
{
'  aaReLoad загружает файл новый анимации
'  "в дескриптор" старой анимации.
'
'  Уведомления теряются, но палитра и окно
'  сохраняются.
}

{ --------------------------------------------------------------------------- }
function aaUnload(hAa: AAHandle): boolean;
external 'AAPLAY';
{
'  aaUnload выгружает загруженную анимацию.
'
'  Возвращается FALSE, если
'  hAa не является дескриптором загруженной анимации.
}

{ --------------------------------------------------------------------------- }
function aaPlay(hAa: AAHandle) : boolean;
external 'AAPLAY';
{
'  aaPlay воспроизводит загруженную анимацию.
'
'  Возвращается TRUE, если после возврата aaPlay анимация не останавливается.
}

{ --------------------------------------------------------------------------- }
function aaNotify(hAa: AAHandle; lPosition, lParam: longint) : boolean;
external 'AAPLAY';
{
'  aaNotify позволяет извещать приложение о воспроизведении
'  определенных кадров анимации.
'
'  lPosition -позиция, в которой должно происходить уведомление.
'
'  wParam для данного сообщения - hAa, а lParam копируется из этого вызова.
'
'  Возвращается TRUE, если уведомление установлено.
}

{ --------------------------------------------------------------------------- }
function aaCancel(hAa: AAHandle; lLoPos, lHiPos: longint) : word;
external 'AAPLAY';
{
'  aaCancel позволяет приложению отменить уведомления, установленные aaNotify.
'
'  lLoPos и lHiPos задает верхний и нижний предел позициям.
'
'  Возвращает количество отмененных уведомлений.
}

{ --------------------------------------------------------------------------- }
function aaStop(hAa: AAHandle) : boolean;
external 'AAPLAY';
{
'  aaStop прекращает воспроизведение анимации.
'
'  При остановке воспроизведения aaStop возвращает TRUE.
}

{ --------------------------------------------------------------------------- }
function aaPause(hAa: AAHandle) : boolean;
external 'AAPLAY';
{
'  aaPause приостанавливает воспроизведение.
'
'  Возвращается TRUE, если после возврата aaPause анимация переходит в режим паузы.
'
'  Для продолжения воспроизведения анимации используйте aaPlay.
}

{ --------------------------------------------------------------------------- }
function aaPrompt(hAa: AAHandle; lpName: PChar) : boolean;
external 'AAPLAY';
{
'  aaPrompt позволяет выводить диалог для получения данных от пользователя.
'
'  При получении данных дескриптор меняется, и, таким образом, вступают
'  в силу новые параметры. Старый дескриптор не уничтожается до тех пор,
'  пока не будет создан новый.
'
'  Если новый дескриптор не может быть создан, aaPrompt возвращает NULL,
'  в противном случае возвращается новый дескриптор.
}

{ --------------------------------------------------------------------------- }
function aaGetParm(hAa: AAHandle; wType: word) : longint;
external 'AAPLAY';
{
'  aaGetParm возвращает информацию об анимации.
'
'  Некоторая информация может быть установлена с помощью aaSetParm,
'  и другая информация - информация о состоянии, поддерживаемая AAPLAY.
}

{ --------------------------------------------------------------------------- }
function aaGetParmIndirect(hAa: AAHandle; lpAp: AAPARMSPtr; wSize: word)
: boolean; external 'AAPLAY';
{
'  aaGetParmIndirect возвращает ту же информацию, что и aaGetParm,
'  в структуре, удобной для легкого доступа из приложений Visual Basic.
}

{ --------------------------------------------------------------------------- }
function aaSetParm(hAa: AAHandle; wType: word; wValue1, lValue2: longint)
: AAHandle; external 'AAPLAY';
{
'  aaSetParm устанавливает информацию для анимации
}

{ --------------------------------------------------------------------------- }
function aaSetParmIndirect(hAa: AAHandle; dwType: longint; lpAp: AAPARMSPtr;
wMask: word): boolean; external 'AAPLAY';
{
'  aaSetParmIndirect устанавливает параметры анимации из структуры.
}

{ --------------------------------------------------------------------------- }
function aaShow(hAa: AAHandle; bShow: boolean) : boolean;
external 'AAPLAY';
{
'  aaShow позволяет показать в окне отдельный кадр анимации.
'
'  Mode определяет способ рисования анимации.
'
'  Параметры окна возможно задать с помощью aaSetParm или aaSetParmIndirect.
'
'  aaShow возвращает TRUE, если анимация была отрисована без ошибок.
}

{ --------------------------------------------------------------------------- }
function aaSound(hAa: AAHandle; device, ffile: PChar; wMode: word)
: boolean; external 'AAPLAY';
{
'  aaSound открывает и закрывает канал звукового сопровождения анимации.
'
'  Звуковой канал будет открыт, если аргумент file не будет равен null
'  и не будет пустым, в противном случае канал будет закрыт.
'
'  Если устройство равно null, то для выбора подходящего устройства
'  используется формат файла.
}

{ --------------------------------------------------------------------------- }
function aaGetFile(dwFlags: word; lpszPath: PChar; wBufLen: word;
lpszDriver: PChar; wDrvLen: word) : integer; external 'AAPLAY';
{
' Открывает системного диалоговое окно стандартного типа ("открыть файл"),
' предлагающее пользователю выбрать файл.
'
' <dwFlags> определяет характеристики диалогового окна.
' Список возможных флагов:
'       AA_GETFILE_MUSTEXIST    Выбранный файл должен удовлетворять условиям
'                               флагов OpenFile(), в противном случае диалог
'                               издаст системный звук.
'       AA_GETFILE_NOSHOWSPEC   НЕ показывать путь в поле редактирования.
'                               По умолчанию путь к файлу показывается.
'       AA_GETFILE_SAVE         Кнопка Ok имеет заголовок "Save".
'       AA_GETFILE_OPEN         Кнопка Ok имеет заголовок "Open".
'       AA_GETFILE_USEFILE      Взять имя файла из параметра lpszPath
'       AA_GETFILE_UDEDIR       Взять каталог из параметра lpszPath
'       AA_GETFILE_SOUND        Получить звуковой файл и драйвер
'       AA_GETFILE_SCRIPT       Получить файл со скриптом
'       AA_GETFILE_ANIMATION    Получить файл анимации (без скриптов)
'
' <lpszPath> - строковый буфер LPSTR, куда после выполнения диалога
' пишется полное имя пути.
' <wBufLen> - длина данного буфера.
'
' <lpszDriver> - строковый буфер LPSTR для хранения выбранного
' звукового устройства.
' <wDrvLen> - длина данного буфера.
'
' Возвращаемые значения: 0, если была нажата кнопка Cancel
'                        -1, если OpenFile() потерпело неудачу,
'                        а AA_GETFILE_MUSTEXIST не определен.
'                        В противном случае возвращается дескриптор DOS-файла.
'                        При возврате из aaOpenFile данный дескриптор "не открыт".
}

{ --------------------------------------------------------------------------- }
function aaSave(hAa: AAHandle; wMode: word) : integer;
external 'AAPLAY';
{
'  Сохранение скрипта
}

{ --------------------------------------------------------------------------- }

{$F-}
{ Окончание внешних вызовов 'AAPLAY.DLL' }
{ =========================================================================== }

end.

{ =========================================================================== }

Dr Paul Kuczora.

--------------------------------------------------------------------------------
Paul Kuczora c home.london.uk
(на создание файла потрачен один день) [001137]



Копирование содержимого экрана на форму


    var Image3: TImage;

procedure TSaverForm.CopyScreen;
var
DeskTopDC: HDc; DeskTopCanvas: TCanvas; DeskTopRect: TRect; begin
Image3 := TImage.Create(SaverForm); With Image3 do begin Height := Screen.Height; Width := Screen.Width; end; Image3.Canvas.copymode := cmSrcCopy; DeskTopDC := GetWindowDC(GetDeskTopWindow); DeskTopCanvas := TCanvas.Create; DeskTopCanvas.Handle := DeskTopDC; Image3.Canvas.CopyRect(Image3.Canvas.ClipRect, DeskTopCanvas, DeskTopCanvas.ClipRect); Image2.Picture.Assign(Image3.Picture); {image2 расположен на целевой форме и выровнен по области клиента} end;

procedure TSaverForm.FormClose(Sender: TObject; var Action: TCloseAction);
begin
Image3.Free; end;

В настоящее время я также разбираюсь в других ответах на мой вопрос.

Попробуйте следующий HAX 244, взятый из Авг/Сен номера журнала Visual Developer. Это работает, и работает хорошо.

    { смотри текстовое описание за последним END. }
unit Scrncap;

interface
uses
WinTypes, WinProcs, Forms, Classes, Graphics;

function CaptureScreenRect( ARect: TRect ): TBitmap;
function CaptureScreen: TBitmap;
function CaptureClientImage( Control: TControl ): TBitmap;
function CaptureControlImage( Control: TControl ): TBitmap;

implementation

{ используем следующий код для захвата прямоугольной области экрана }
function CaptureScreenRect( ARect: TRect ): TBitmap;
var ScreenDC: HDC;
begin
Result := TBitmap.Create; with Result, ARect do begin Width := Right - Left; Height := Bottom - Top;
ScreenDC := GetDC( 0 ); try BitBlt( Canvas.Handle, 0, 0, Width, Height, ScreenDC, Left, Top, SRCCOPY); finally ReleaseDC( 0, ScreenDC ); end; end; end;

{ используем следующий код для захвата целого экрана }
function CaptureScreen: TBitmap;
begin
with
Screen do Result := CaptureScreenRect( Rect( 0, 0, Width, Height )); end;

{ используем следующий код для захвата клиентской области формы или элемента управления...}
function CaptureClientImage( Control: TControl ): TBitmap;
begin
with
Control, Control.ClientOrigin do Result := CaptureScreenRect( Bounds( X, Y, ClientWidth,
ClientHeight )); end;

{ используйте следующий код для захвата целой формы или элемента управления  }
function CaptureControlImage( Control: TControl ): TBitmap;
begin
with
Control do if Parent = nil then Result := CaptureScreenRect( Bounds( Left, Top, Width, Height )) else with Parent.ClientToScreen( Point( Left, Top )) do Result := CaptureScreenRect( Bounds( X, Y, Width, Height )); end;

end.

{
Источник:  Visual Developer, HAX #244, Авг/Сент 1996

захват экрана с помощью Delphi

В Delphi, если вы хотите получить изображение клиентской области формы, необходимо вызвать GetFormlmage. Но иногда возникает необходимость получения снимка формы целиком, вместе с заголовком, контуром и всем содержимым. Или целиком всего экрана. Если бы у вас был дефицит времени, мы бы в этом случае посоветовали показывать диалоговое окно с надписью "Теперь нажмите кнопку Print Screen!", после чего работать с изображением, помещенным в буфер обмена.

Но мы никуда не спешим. Комбинирование хостов Delphi с несколькими функциями GDI сводят задачу получения снимка экрана всего к одной строчке кода.

CaptureScreenRect, в листинге 1, демонстрирует это. Код получает экранный контекст устройства с помощью GetDC(O), и затем копирует прямоугольную область этого DC на холст изображения (Bitmap). Для копирования используется BitBlt. Смысл использования BitBlt (и любой функции GDI) в том, что Delphi помнит, что дескриптор холста есть DC, необходимый Windows.

Остальные функции копирования экрана в листинге 1 захватывают прямоугольник и отдает реальную работу на откуп CaptureScreenRect. CaptureScreen захватывает для прямоугольника целый экран. CaptureClientImage и CaptureControlImage захватывают прямоугольник области клиента и элемента управления, соответственно.

Эти четыре функции могут быть использованы для захвата любой произвольной области экрана, а также экранных областей форм, кнопок, полей редактирования, ComboBox'ов и пр.. Не забывайте после работы освобождать используемые вами картинки (Bitmap). }

[001733]



Можно ли запустить OpenGL под Windows'95, и как поставлять его с программой?


Nomadic советует:

Надо сразу отметить, что для работы Microsoft OpenGL 1.1 требуется только наличие в системе двух динамических библиотек. Они различны для Windows 95 и для Windows NT. Они всегда инсталлируется вместе с системой, если эта система - Windows 95 OSR2 или более поздняя, или если это Windows NT. Однако, если Вы столкнулись с машиной, где OpenGL отсутствует (Windows 95 OSR1 и более ранние), то достаточно их взять из диcтpибyтива OSR2 (GLU32.DLL и OPENGL32.DLL) и записать в GetSystemDirectory - и запycкайте OpenGL-приложения на здоpовье.

Более эффективную реализацию OpenGL для Win32 от фирмы SGI я бы советовал стянуть с www.sgi.com или www.opengl.org (SGI OpenGL for Windows). Кроме того, оттуда же советую скачать дополнительную библиотеку функций-утилит, позволяющую упростить работу в OpenGL (GLUT). Возможно, что Вам понравится какой-нибудь API более высокого уровня, типа SCiTech MGL (www.scitechsoft.com). [001220]



Назначение палитры Bitmap


Если вы рисуете на TImage....

Во-первых, вам нужно использовать Image1.Picture.bitmap, а не Image.Canvas. Причина кроется в том, что Image1.Picture.Bitmap имеет палитру, в Timage нет. Затем палитру необходимо назначить. Вот пример:

    //Устанавливаем Width и Height перед использованием Image1.Picture c Bitmap Canvasvar
Bitmap: TBitmap; begin

Bitmap:=TBitmap.Create; Bitmap.LoadfromFile({'Whatever.bmp'});
With Image2.Picture.bitmap do Begin Width:=Bitmap.Width; height:=Bitmap.Height; Palette:=Bitmap.Palette; Canvas.draw(0,0,bitmap); Refresh; end; end;

Если вы рисуете на канве формы...

    Canvas.Draw(0,0,Bitmap); SelectPalette(Form1.Canvas.handle,Bitmap.Palette,True); RealizePalette(Form1.Canvas.Handle);

[001799]



Несколько программ для работы с точечной графикой (2D и 3D)


    unit Functs;

interface

uses
WinTypes, Classes, Graphics, SysUtils;
type
TPoint2D = record X, Y: Real; end; TPoint3D = record X, Y, Z: Real; end;
function Point2D(X, Y: Real): TPoint2D;
function RoundPoint(P: TPoint2D): TPoint;
function FloatPoint(P: TPoint): TPoint2D;
function Point3D(X, Y, Z: Real): TPoint3D;
function Angle2D(P: TPoint2D): Real;
function Dist2D(P: TPoint2D): Real;
function Dist3D(P: TPoint3D): Real;
function RelAngle2D(PA, PB: TPoint2D): Real;
function RelDist2D(PA, PB: TPoint2D): Real;
function RelDist3D(PA, PB: TPoint3D): Real;
procedure Rotate2D(var P: TPoint2D; Angle2D: Real);
procedure RelRotate2D(var P: TPoint2D; PCentr: TPoint2D; Angle2D: Real);
procedure Move2D(var P: TPoint2D; Angle2D, Distance: Real);
function Between(PA, PB: TPoint2D; Preference: Real): TPoint2D;
function DistLine(A, B, C: Real; P: TPoint2D): Real;
function Dist2P(P, P1, P2: TPoint2D): Real;
function DistD1P(DX, DY: Real; P1, P: TPoint2D): Real;
function NearLine2P(P, P1, P2: TPoint2D; D: Real): Boolean;
function AddPoints(P1, P2: TPoint2D): TPoint2D;
function SubPoints(P1, P2: TPoint2D): TPoint2D;

function Invert(Col: TColor): TColor;
function Dark(Col: TColor; Percentage: Byte): TColor;
function Light(Col: TColor; Percentage: Byte): TColor;
function Mix(Col1, Col2: TColor; Percentage: Byte): TColor;
function MMix(Cols: array of TColor): TColor;
function Log(Base, Value: Real): Real;
function Modulator(Val, Max: Real): Real;
function M(I, J: Integer): Integer;
function Tan(Angle2D: Real): Real;
procedure Limit(var Value: Integer; Min, Max: Integer);
function Exp2(Exponent: Byte): Word;
function GetSysDir: String;
function GetWinDir: String;

implementation

function
Point2D(X, Y: Real): TPoint2D;
begin
Point2D.X := X; Point2D.Y := Y; end;

function RoundPoint(P: TPoint2D): TPoint;
begin
RoundPoint.X := Round(P.X); RoundPoint.Y := Round(P.Y); end;

function FloatPoint(P: TPoint): TPoint2D;
begin
FloatPoint.X := P.X; FloatPoint.Y := P.Y; end;

function Point3D(X, Y, Z: Real): TPoint3D;
begin
Point3D.X := X; Point3D.Y := Y; Point3D.Z := Z; end;

function Angle2D(P: TPoint2D): Real;
begin
if
P.X = 0 then begin if P.Y > 0 then Result := Pi / 2; if P.Y = 0 then Result := 0; if P.Y < 0 then Result := Pi / -2; end else Result := Arctan(P.Y / P.X);
if P.X < 0 then begin if P.Y < 0 then Result := Result + Pi; if P.Y >= 0 then Result := Result - Pi; end;
If Result < 0 then Result := Result + 2 * Pi; end;

function Dist2D(P: TPoint2D): Real;
begin
Result := Sqrt(P.X * P.X + P.Y * P.Y); end;

function Dist3D(P: TPoint3D): Real;
begin
Dist3d := Sqrt(P.X * P.X + P.Y * P.Y + P.Z * P.Z); end;

function RelAngle2D(PA, PB: TPoint2D): Real;
begin
RelAngle2D := Angle2D(Point2D(PB.X - PA.X, PB.Y - PA.Y)); end;

function RelDist2D(PA, PB: TPoint2D): Real;
begin
Result := Dist2D(Point2D(PB.X - PA.X, PB.Y - PA.Y)); end;

function RelDist3D(PA, PB: TPoint3D): Real;
begin
RelDist3D := Dist3D(Point3D(PB.X - PA.X, PB.Y - PA.Y, PB.Z - PA.Z)); end;

procedure Rotate2D(var P: TPoint2D; Angle2D: Real);
var
Temp: TPoint2D; begin
Temp.X := P.X * Cos(Angle2D) - P.Y * Sin(Angle2D); Temp.Y := P.X * Sin(Angle2D) + P.Y * Cos(Angle2D); P := Temp; end;

procedure RelRotate2D(var P: TPoint2D; PCentr: TPoint2D; Angle2D: Real);
var
Temp: TPoint2D; begin
Temp := SubPoints(P, PCentr); Rotate2D(Temp, Angle2D); P := AddPoints(Temp, PCentr); end;

procedure Move2D(var P: TPoint2D; Angle2D, Distance: Real);
var
Temp: TPoint2D; begin
Temp.X := P.X + (Cos(Angle2D) * Distance); Temp.Y := P.Y + (Sin(Angle2D) * Distance); P := Temp; end;

function Between(PA, PB: TPoint2D; Preference: Real): TPoint2D;
begin
Between.X := PA.X * Preference + PB.X * (1 - Preference); Between.Y := PA.Y * Preference + PB.Y * (1 - Preference); end;

function DistLine(A, B, C: Real; P: TPoint2D): Real;
begin
Result := (A * P.X + B * P.Y + C) / Sqrt(Sqr(A) + Sqr(B)); end;

function Dist2P(P, P1, P2: TPoint2D): Real;
begin
Result := DistLine(P1.Y - P2.Y, P2.X - P1.X, -P1.Y * P2.X + P1.X * P2.Y, P); end;

function DistD1P(DX, DY: Real; P1, P: TPoint2D): Real;
begin
Result := DistLine(DY, -DX, -DY * P1.X + DX * P1.Y, P); end;

function NearLine2P(P, P1, P2: TPoint2D; D: Real): Boolean;
begin
Result := False; if DistD1P(-(P2.Y - P1.Y), P2.X - P1.X, P1, P) * DistD1P(-(P2.Y - P1.Y), P2.X - P1.X, P2, P) <= 0 then if Abs(Dist2P(P, P1, P2)) < D then Result := True; end;

function AddPoints(P1, P2: TPoint2D): TPoint2D;
begin
AddPoints := Point2D(P1.X + P2.X, P1.Y + P2.Y); end;

function SubPoints(P1, P2: TPoint2D): TPoint2D;
begin
SubPoints := Point2D(P1.X - P2.X, P1.Y - P2.Y); end;

function Invert(Col: TColor): TColor;
begin
Invert := not Col; end;

function Dark(Col: TColor; Percentage: Byte): TColor;
var
R, G, B: Byte; begin
R := GetRValue(Col); G := GetGValue(Col); B := GetBValue(Col); R := Round(R * Percentage / 100); G := Round(G * Percentage / 100); B := Round(B * Percentage / 100); Dark := RGB(R, G, B); end;

function Light(Col: TColor; Percentage: Byte): TColor;
var
R, G, B: Byte; begin
R := GetRValue(Col); G := GetGValue(Col); B := GetBValue(Col); R := Round(R * Percentage / 100) + Round(255 - Percentage / 100 * 255); G := Round(G * Percentage / 100) + Round(255 - Percentage / 100 * 255); B := Round(B * Percentage / 100) + Round(255 - Percentage / 100 * 255); Light := RGB(R, G, B); end;

function Mix(Col1, Col2: TColor; Percentage: Byte): TColor;
var
R, G, B: Byte; begin
R := Round((GetRValue(Col1) * Percentage / 100) + (GetRValue(Col2) * (100 - Percentage) / 100)); G := Round((GetGValue(Col1) * Percentage / 100) + (GetGValue(Col2) * (100 - Percentage) / 100)); B := Round((GetBValue(Col1) * Percentage / 100) + (GetBValue(Col2) * (100 - Percentage) / 100)); Mix := RGB(R, G, B); end;

function MMix(Cols: array of TColor): TColor;
var
I, R, G, B, Length: Integer; begin
Length := High(Cols) - Low(Cols) + 1; R := 0; G := 0; B := 0; for I := Low(Cols) to High(Cols) do begin R := R + GetRValue(Cols[I]); G := G + GetGValue(Cols[I]); B := B + GetBValue(Cols[I]); end; R := R div Length; G := G div Length; B := B div Length; MMix := RGB(R, G, B); end;

function Log(Base, Value: Real): Real;
begin
Log := Ln(Value) / Ln(Base); end;

function Power(Base, Exponent: Real): Real;
begin
Power := Ln(Base) * Exp(Exponent); end;

function Modulator(Val, Max: Real): Real;
begin
Modulator := (Val / Max - Round(Val / Max)) * Max; end;

function M(I, J: Integer): Integer;
begin
M := ((I mod J) + J) mod J; end;

function Tan(Angle2D: Real): Real;
begin
Tan := Sin(Angle2D) / Cos(Angle2D); end;

procedure Limit(var Value: Integer; Min, Max: Integer);
begin
if
Value < Min then Value := Min; if Value > Max then Value := Max; end;

function Exp2(Exponent: Byte): Word;
var
Temp, I: Word; begin
Temp := 1; for I := 1 to Exponent do Temp := Temp * 2; Result := Temp; end;

function GetSysDir: String;
var
Temp: array[0..255] of Char; begin
GetSystemDirectory(Temp, 256); Result := StrPas(Temp); end;

function GetWinDir: String;
var
Temp: array[0..255] of Char; begin
GetWindowsDirectory(Temp, 256); Result := StrPas(Temp); end;

end.

[000120]



Отображение ломанной линии


Как мне вывести ломанную линию на холсте, если я не знаю размер массива (количество точек) до момента запуска программы??? По-моему, это невозможно. Просветите меня!

Недавно я решал аналогичную проблему при кодировании ReportPrinter и не нашел хорошего решения для создания открытого массива параметров заданного размера. Решение, которое я, наконец, использовал, заключалось в хитрой комбинации функции polyline и polygon с ассемблерным кодом. Я публикую исходный код, поскольку думаю что он будет полезен, пока Borland не создаст стандартные и простые методы для работы с массивами заданного размера.

    type PPointArr = ^TPointArr; TPointArr = array[0..16380] of TPoint;
var I1: integer; Elements: word; PointArr: PPointArr;
begin GetMem(PointArr,(Elements + 1) * SizeOf(TPoint)); try For I1 := 0 to Elements do begin PointArr^[I1].X := ReadNextXValue; PointArr^[I1].Y := ReadNextYValue; end; { for }
{ Вызов Polygon(PointArr^), но только с Elements+1-элеменами в открытом массиве } asm les di,PointArr { Помещаем указатель на PointArr } push es push di push Elements { Помещаем High(PointArr^) } les di,self { Помещаем указатель self } push es push di les di,es:[di] { Вызов self.Polygon } call Polygon end; { asm } finally FreeMem(PointArr,(Elements + 1) * SizeOf(TPoint)); end; { try } end;

Надеюсь это поможет.

Jim Gunkel
Nevrona Designs [000717]



Получение DC элемента управления


    {Bitmap в TImage}
HDC:=Image1.PICTURE.bitmap.canvas.handle;

DC - что-нибудь с Canvas.handle. [001815]



Получение контекста устройства для элемента управления


    {Bitmap в TImage}
HDC := TImage.Picture.bitmap.canvas.handle;

DC - что нибудь с Canvas.handle. [001521]



Поворот изображения на 90 градусов


Новый модуль имеет три программы: RotateBitmap90DegreesClockwise, RotateBitmap90DegreesCounterClockwise, и RotateBitmap180Degrees. Все три используют TBitmap как переменную и вращают его согласно своему названию.

Два предостережения: Это все еще не совсем работает в Delphi3. Появляется какой-то шум на краях изображения. Мне кажется это из-за какой-то ошибки в методе LoadFromStream объекта TBitmap, но это может быть и моей ошибкой. Тем не менее есть другие решения, связанные с использованием свойства ScanLine, так что эта проблема решается. Во-вторых, этот алгоритм не работает с сжатыми RLE-алгоритмом изображениями. 4- и 8-битные (по разрешению) изображения могут быть декодированы и хранится в памяти: на случай, если они потребуются, у нас есть их дескриптор. К тому же, если изображение сжато, можно просто получить дескриптор канвы с нормальным изображением:

    ABitmap.Canvas.Handle;

Этим мы также назначаем контекст устройства (то есть экрана), и, вероятно, сможем обрабатывать изображения вплоть до 24-битного формата. Что-то вроде компромисного решения.

Во всяком случае это работает у меня в Delphi 1 и 2 с черно-белыми, 4-, 8-, 16-, 24-, и 32-битными изображениями (но не с 4- и 8-битными изображениями, сжатыми RLE-алгоритмом, как я уже говорил выше).

    unit bmpRot;

interface

uses
(*$IFDEF Win32*) Windows, (*$ELSE*) WinTypes, WinProcs, (*$ENDIF*) Classes, Graphics;
procedure RotateBitmap90DegreesCounterClockwise(var ABitmap: TBitmap);
procedure RotateBitmap90DegreesClockwise(var ABitmap: TBitmap);
procedure RotateBitmap180Degrees(var ABitmap: TBitmap);

implementation

uses
Dialogs;
(*$IFNDEF Win32*)
type
DWORD = LongInt; TSelOfs = record L, H: Word; end;
procedure Win16Dec(var P: Pointer; const N: LongInt); forward;

procedure Win16Inc(var P: Pointer; const N: LongInt);
begin
if
N < 0 then Win16Dec(P, -N) else if N > 0 then begin Inc( TSelOfs(P).H, TSelOfs(N).H * SelectorInc ); Inc( TSelOfs(P).L, TSelOfs(N).L ); if TSelOfs(P).L < TSelOfs(N).L then Inc( TSelOfs(P).H, SelectorInc ); end; end;

procedure Win16Dec(var P: Pointer; const N: LongInt);
begin
if
N < 0 then Win16Inc(P, -N) else if N > 0 then begin if TSelOfs(N).L > TSelOfs(P).L then Dec( TSelOfs(P).H, SelectorInc ); Dec( TSelOfs(P).L, TSelOfs(N).L ); Dec( TSelOfs(P).H, TSelOfs(N).H * SelectorInc ); end; end;

(*
procedure HugeShift; far; external 'KERNEL' index 113;
procedure Win16Dec(var P: Pointer; const N: LongInt); forward;

procedure Win16Inc(var HugePtr: Pointer; Amount: LongInt);
procedure HugeInc; assembler; asm mov ax, Amount.Word[0]        { Сохраняем сумму в DX:AX. } mov dx, Amount.Word[2] les bx, HugePtr               { Получаем ссылку на HugePtr. } add ax, es:[bx]               { Добавление коррекции. } adc dx, 0                     { Распространяем перенос на наибольшую величину суммы. } mov cx, Offset HugeShift shl dx,                       { Перемещаем наибольшую величину суммы для сегмента. } add es:[bx+2], dx             { Увеличиваем сегмент HugePtr. } mov es:[bx], ax end;
begin
if Amount > 0 then HugeInc else if Amount < 0 then Win16Dec(HugePtr, -Amount); end;

procedure Win16Dec(var P: Pointer; const N: LongInt);
begin
if N < 0 then Win16Inc(P, -N) else if N > 0 then begin if TSelOfs(N).L > TSelOfs(P).L then Dec( TSelOfs(P).H, SelectorInc ); Dec( TSelOfs(P).L, TSelOfs(N).L ); Dec( TSelOfs(P).H, TSelOfs(N).H * SelectorInc ); end; end;
*)

(*$ENDIF*)

procedure RotateBitmap90DegreesCounterClockwise(var ABitmap: TBitmap);
const
BitsPerByte = 8;
var
{ Целая куча переменных. Некоторые имеют дело только с одно- и четырех-битовыми изображениями, другие только с восемью- и 24-битовыми, а некоторые с обоими. Любая переменная, оканчивающаяся символом 'R', имеет отношение к вращению изображения, например если MemoryStream содержит исходное изображение, то MemoryStreamR - повернутое. } PbmpInfoR: PBitmapInfoHeader; bmpBuffer, bmpBufferR: PByte; MemoryStream, MemoryStreamR: TMemoryStream; PbmpBuffer, PbmpBufferR: PByte; BytesPerPixel, PixelsPerByte: LongInt; BytesPerScanLine, BytesPerScanLineR: LongInt; PaddingBytes: LongInt; BitmapOffset: LongInt; BitCount: LongInt; WholeBytes, ExtraPixels: LongInt; SignificantBytes, SignificantBytesR: LongInt; ColumnBytes: LongInt; AtLeastEightBitColor: Boolean; T: LongInt;
procedure NonIntegralByteRotate; (* вложение *)
{
Эта программа осуществляет поворот изображений с разрешением меньшим, чем 8 бит на пиксел, а имеено: черно-белые (1-бит) и 16-цветные (4-бит) изображения. Имейте в виду, что такие вещи, как 2-битные изображения также могли бы вращаться, но Microsoft не включил данный формат в свои спецификации и не поддерживает его. }
var
X, Y: LongInt; I: LongInt; MaskBits, CurrentBits: Byte; FirstMask, LastMask: Byte; PFirstScanLine: PByte; FirstIndex, CurrentBitIndex: LongInt; ShiftRightAmount, ShiftRightStart: LongInt;
begin
(*$IFDEF Win32*) Inc(PbmpBuffer, BytesPerScanLine * (PbmpInfoR^.biHeight - 1) ); (*$ELSE*) Win16Inc( Pointer(PbmpBuffer), BytesPerScanLine * (PbmpInfoR^.biHeight - 1) ); (*$ENDIF*)
{ PFirstScanLine движется вдоль первой линии чередования bmpBufferR. } PFirstScanLine := bmpBufferR;
{ Устанавливаем индексирование. } FirstIndex := BitsPerByte - BitCount;
{ Устанавливаем битовые маски:
Для черно-белого изображения, LastMask  := 00000001    и FirstMask := 10000000
Для 4-битного изображения, LastMask  := 00001111    и FirstMask := 11110000
Зададим значения CurrentBits и MaskBits, так как мы будем перемещаться по ним: Для монохромных изображений: 10000000, 01000000, 00100000, 00010000, 00001000, 00000100, 00000010, 00000001 Для 4-битных изображений: 11110000, 00001111
CurrentBitIndex определяет расстояние от крайнего правого бита до позиции CurrentBits. Например, если мы находимся в одиннадцатой колонке черно-белого изображения, CurrentBits равен 11 mod 8 := 3, или 3-й самый левый бит. Таким образом, крайне правый бит должен переместиться на четыре позиции, чтобы попасть на позицию CurrentBits. CurrentBitIndex как раз и хранит такое значение. } LastMask := 1 shl BitCount - 1; FirstMask := LastMask shl FirstIndex;
CurrentBits := FirstMask; CurrentBitIndex := FirstIndex;
ShiftRightStart := BitCount * (PixelsPerByte - 1);
{ Вот мясо. Перебираем в цикле все пиксели и соответственно вращаем. } { Помните что DIB'ы имеют происхождение противоположное DDB'сам. }
{ Счетчик Y указывает на текущую строчку исходного изображения. } for Y := 1 to PbmpInfoR^.biHeight do begin PbmpBufferR := PFirstScanLine;
{ Счетчик X указывает на текущую колонку пикселей исходного изображения. Здесь мы имеем дело только с полностью заполненными байтами. Обработка 'частично заполненных' байтов происходит ниже. } for X := 1 to WholeBytes do begin { Выбираем биты, начиная с 10000000 для черно-белых и заканчивая 11110000 для 4-битных изображений. } MaskBits := FirstMask; { ShiftRightAmount - сумма, необходимая для перемещения текущего байта через весь путь (помните, я об этом говорил выше) в правую часть. } ShiftRightAmount := ShiftRightStart; for I := 1 to PixelsPerByte do begin { Вот гарнир. Берем текущий байт вращаемого изображения и маскируем его с not CurrentBits. Гасятся нулями только биты CurrentBits, сам байт перемещается без изменений. Пример: Для черно-белого изображения, если бы мы находились в 11-й колонке (см. пример выше), мы должны нулем погасить 3-й слева бит, то есть мы должны использовать PbmpBufferR^ и 11011111.
Теперь рассмотрим наш текущий исходный байт. Для черно-белых изображений мы организуем цикл с шагом через бит, в общей сложности для восьми пикселей. Для 4-битных изображений мы делаем цикл с обработкой четырех битов за проход для двух пикселей. В любом случае мы делаем это через маскирование с MaskBits ('PbmpBuffer^ и MaskBits'). Теперь нам нужно получить бит(ы) из той колонки(ок), на которую отобразится CurrentBits. Мы это делаем с помощью перемещения их в крайне правую часть байта ('shr ShiftRightAmount'), затем сдвигая их налево с помощью вышеупомянутого CurrentBitIndex ('shl CurrentBitIndex'). Дело в том, что хотя перемещение вправо с параметром -n должно быть просто перемещением налево с параметром +n, в Delphi это не работает. Итак, мы начинаем с первого байта, перемещая пиксели в правую часть насколько это возможно незанятыми позициями.
Наконец, мы имеем наш исходный бит(ы), перемещенный на нужное место с погашенными нулями битами. Последнее делаем непосредственно или с помощью PbmpBufferR^ (гасим биты в CurrentBits, помните?).
Мда... &quotПросто&quot. Ладно, поехали дальше. }
PbmpBufferR^ := ( PbmpBufferR^ and not CurrentBits ) or ( (PbmpBuffer^ and MaskBits) shr ShiftRightAmount shl CurrentBitIndex );
{ Сдвигаем MaskBits для следующей итерации. } MaskBits := MaskBits shr BitCount; (*$IFDEF Win32*) { Перемещаем наш указатель на буфер вращаемого изображения на одну линию чередования. } Inc(PbmpBufferR, BytesPerScanLineR); { Нам не нужно перемещаться непосредственно вправо в течение некоторого времени. } Dec(ShiftRightAmount, BitCount); (*$ELSE*) Win16Inc( Pointer(PbmpBufferR), BytesPerScanLineR ); Win16Dec( Pointer(ShiftRightAmount), BitCount ); (*$ENDIF*) end; (*$IFDEF Win32*) Inc(PbmpBuffer); (*$ELSE*) Win16Inc( Pointer(PbmpBuffer), 1 ); (*$ENDIF*) end;
{ Если есть "частично заполненный" байт, самое время о нем позаботиться. } if ExtraPixels <> 0 then begin { Делаем такие же манипуляции, как в цикле выше. } MaskBits := FirstMask; ShiftRightAmount := ShiftRightStart; for I := 1 to ExtraPixels do begin PbmpBufferR^ := ( PbmpBufferR^ and not CurrentBits ) or ( (PbmpBuffer^ and MaskBits) shr ShiftRightAmount shl CurrentBitIndex );
MaskBits := MaskBits shr BitCount; (*$IFDEF Win32*) Inc(PbmpBufferR, BytesPerScanLineR); (*$ELSE*) Win16Inc( Pointer(PbmpBufferR), BytesPerScanLineR ); (*$ENDIF*) Dec(ShiftRightAmount, BitCount); end; (*$IFDEF Win32*) Inc(PbmpBuffer); (*$ELSE*) Win16Inc( Pointer(PbmpBuffer), 1 ); (*$ENDIF*) end;
(*$IFDEF Win32*) { Пропускаем заполнение. } Inc(PbmpBuffer, PaddingBytes); { Сохраняем только что просмотренную линию чередования и переходим к следующей для получения набора очередной строки. } Dec(PbmpBuffer, BytesPerScanLine shl 1); (*$ELSE*) Win16Inc( Pointer(PbmpBuffer), PaddingBytes ); Win16Dec( Pointer(PbmpBuffer), BytesPerScanLine shl 1 ); (*$ENDIF*)
if CurrentBits = LastMask then begin { Мы в конце этого байта. Начинаем с другой колонки. } CurrentBits := FirstMask; CurrentBitIndex := FirstIndex; { Идем вниз колонки вращаемого изображения , но одну колонку пропускаем. } (*$IFDEF Win32*) Inc(PFirstScanLine); (*$ELSE*) Win16Inc( Pointer(PFirstScanLine), 1 ); (*$ENDIF*) end else begin { Продолжаем заполнять этот байт. } CurrentBits := CurrentBits shr BitCount; Dec(CurrentBitIndex, BitCount); end; end; end; { procedure NonIntegralByteRotate (* вложение *) }

procedure IntegralByteRotate; (* вложение *)
var
X, Y: LongInt; (*$IFNDEF Win32*) I: Integer; (*$ENDIF*)
begin
{ Перемещаем PbmpBufferR в последнюю колонку первой линии чередования bmpBufferR. } (*$IFDEF Win32*) Inc(PbmpBufferR, SignificantBytesR - BytesPerPixel); (*$ELSE*) Win16Inc( Pointer(PbmpBufferR), SignificantBytesR - BytesPerPixel ); (*$ENDIF*)
{ Вот мясо. Перебираем в цикле все пиксели и соответственно вращаем. } { Помните что DIB'ы имеют происхождение противоположное DDB'сам. } for Y := 1 to PbmpInfoR^.biHeight do begin for X := 1 to PbmpInfoR^.biWidth do begin { Копируем пиксели. } (*$IFDEF Win32*) Move(PbmpBuffer^, PbmpBufferR^, BytesPerPixel); Inc(PbmpBuffer, BytesPerPixel); Inc(PbmpBufferR, BytesPerScanLineR); (*$ELSE*) for I := 1 to BytesPerPixel do begin PbmpBufferR^ := PbmpBuffer^; Win16Inc( Pointer(PbmpBuffer), 1 ); Win16Inc( Pointer(PbmpBufferR), 1 ); end; Win16Inc( Pointer(PbmpBufferR), BytesPerScanLineR - BytesPerPixel); (*$ENDIF*) end; (*$IFDEF Win32*) { Пропускаем заполнение. } Inc(PbmpBuffer, PaddingBytes); { Идем вверх колонки вращаемого изображения , но одну колонку пропускаем. } Dec(PbmpBufferR, ColumnBytes + BytesPerPixel); (*$ELSE*) Win16Inc( Pointer(PbmpBuffer), PaddingBytes); Win16Dec( Pointer(PbmpBufferR), ColumnBytes + BytesPerPixel); (*$ENDIF*) end; end;

{ Это тело процедуры RotateBitmap90DegreesCounterClockwise. }
begin
{ Никогда сами не вызывайте GetDIBSizes! Это испортит ваше изображение. }
MemoryStream := TMemoryStream.Create;
{ Для работы: Прежде всего установим размер. Это устранит перераспределение памяти для MemoryStream. Вызов GetDIBSizes будет к месту, но, как отмечалось выше, это может исказить ваше изображение. Вызов некоторых API функций вероятно позаботился бы об этом, но это тема отдельного разговора. }
{ Недокументированный метод. Все же программист иногда сродни шаману. } ABitmap.SaveToStream(MemoryStream);
{ Изображение больше не нужно. Создадим новое когда понадобится. } ABitmap.Free;
bmpBuffer := MemoryStream.Memory; { Получаем биты компенсации. Они могут содержать информацию о палитре. } BitmapOffset := PBitmapFileHeader(bmpBuffer)^.bfOffBits;
{ Устанавливаем PbmpInfoR на указатель информационного заголовка исходного изображения. } { Эти заголовки могут немного раздражать, но они необходимы для работы. } (*$IFDEF Win32*) Inc( bmpBuffer, SizeOf(TBitmapFileHeader) ); (*$ELSE*) Win16Inc( Pointer(bmpBuffer), SizeOf(TBitmapFileHeader) ); (*$ENDIF*) PbmpInfoR := PBitmapInfoHeader(bmpBuffer);
{ Устанавливаем bmpBuffer и PbmpBuffer так, чтобы они указывали на биты оригинального изображения. } bmpBuffer := MemoryStream.Memory; (*$IFDEF Win32*) Inc(bmpBuffer, BitmapOffset); (*$ELSE*) Win16Inc( Pointer(bmpBuffer), BitmapOffset ); (*$ENDIF*) PbmpBuffer := bmpBuffer;
{ Имейте в виду, что нам не нужно беспокоиться о совместимости изображений версии 4 и 3, поскольку области, которые мы используем, а именно -- biWidth, biHeight, и biBitCount -- располагаются на один и тех же местах в обоих структурах. Итак, одной проблемой меньше. Изображения версии OS/2, между прочим, при этом гнусно рушатся. Обидно. } with PbmpInfoR^ do begin { ShowMessage('Компрессия := ' + IntToStr(biCompression)); } BitCount := biBitCount; { ShowMessage('BitCount := ' + IntToStr(BitCount)); }
{ ScanLines - "выровненный" DWORD. } BytesPerScanLine := ((((biWidth * BitCount) + 31) div 32) * SizeOf(DWORD)); BytesPerScanLineR := ((((biHeight * BitCount) + 31) div 32) * SizeOf(DWORD));
AtLeastEightBitColor := BitCount >= BitsPerByte; if AtLeastEightBitColor then begin { Нас не должен волновать бит-тильда. Классно. } BytesPerPixel := biBitCount shr 3; SignificantBytes := biWidth * BitCount shr 3; SignificantBytesR := biHeight * BitCount shr 3; { Дополнительные байты необходимы для выравнивания DWORD. } PaddingBytes := BytesPerScanLine - SignificantBytes; ColumnBytes := BytesPerScanLineR * biWidth; end else begin { Одно- или четырех-битовое изображение. Уфф. } PixelsPerByte := SizeOf(Byte) * BitsPerByte div BitCount; { Все количество байтов полностью заполняется информацией о пикселе. } WholeBytes := biWidth div PixelsPerByte; { Обрабатываем любые дополнительные биты, которые могут частично заполнять байт. Например, черно-белое изображение, у которого 14 пикселей описываются каждый соответственно своим байтом, плюс одним дополнительным, у которого на самом деле используются 6 битов, остальное мусор. } ExtraPixels := biWidth mod PixelsPerByte; { Все дополнительные байты -- если имеются -- требуется DWORD-выровнять по линии чередования. } PaddingBytes := BytesPerScanLine - WholeBytes; { Если есть дополнительные биты (то есть имеется 'дополнительный байт'), то один из заполненных байтов уже был принят во внимание. } if ExtraPixels <> 0 then Dec(PaddingBytes); end; { if AtLeastEightBitColor then }
{ TMemoryStream, обслуживающий вращаемые биты. } MemoryStreamR := TMemoryStream.Create; { Устанавливаем размер вращаемого изображения. Может отличаться от исходного из-за выравнивания DWORD. } MemoryStreamR.SetSize(BitmapOffset + BytesPerScanLineR * biWidth); end; { with PbmpInfoR^ do }
{ Копируем заголовки исходного изображения. } MemoryStream.Seek(0, soFromBeginning); MemoryStreamR.CopyFrom(MemoryStream, BitmapOffset);
{ Вот буфер, который мы будем "вращать". } bmpBufferR := MemoryStreamR.Memory; { Пропускаем заголовки, yadda yadda yadda... } (*$IFDEF Win32*) Inc(bmpBufferR, BitmapOffset); (*$ELSE*) Win16Inc( Pointer(bmpBufferR), BitmapOffset ); (*$ENDIF*) PbmpBufferR := bmpBufferR;
{ Едем дальше. } if AtLeastEightBitColor then IntegralByteRotate else NonIntegralByteRotate;
{ Удовлетворяемся исходными битами. } MemoryStream.Free;
{ Теперь устанавливаем PbmpInfoR, чтобы он указывал на информационный заголовок вращаемого изображения. } PbmpBufferR := MemoryStreamR.Memory; (*$IFDEF Win32*) Inc( PbmpBufferR, SizeOf(TBitmapFileHeader) ); (*$ELSE*) Win16Inc( Pointer(PbmpBufferR), SizeOf(TBitmapFileHeader) ); (*$ENDIF*) PbmpInfoR := PBitmapInfoHeader(PbmpBufferR);
{ Меняем ширину с высотой в информационном заголовке вращаемого изображения. } with PbmpInfoR^ do begin T := biHeight; biHeight := biWidth; biWidth := T; biSizeImage := 0; end;
ABitmap := TBitmap.Create;
{ Вращение с самого начала. } MemoryStreamR.Seek(0, soFromBeginning); { Загружаем это снова в ABitmap. } ABitmap.LoadFromStream(MemoryStreamR);
MemoryStreamR.Free; end;

procedure RotateBitmap90DegreesClockwise(var ABitmap: TBitmap);
const
BitsPerByte = 8;
var
{ Целая куча переменных. Некоторые имеют дело только с одно- и четырех-битовыми изображениями, другие только с восемью- и 24-битовыми, а некоторые с обоими. Любая переменная, оканчивающаяся символом 'R', имеет отношение к вращению изображения, например если MemoryStream содержит исходное изображение, то MemoryStreamR - повернутое. } PbmpInfoR: PBitmapInfoHeader; bmpBuffer, bmpBufferR: PByte; MemoryStream, MemoryStreamR: TMemoryStream; PbmpBuffer, PbmpBufferR: PByte; BytesPerPixel, PixelsPerByte: LongInt; BytesPerScanLine, BytesPerScanLineR: LongInt; PaddingBytes: LongInt; BitmapOffset: LongInt; BitCount: LongInt; WholeBytes, ExtraPixels: LongInt; SignificantBytes: LongInt; ColumnBytes: LongInt; AtLeastEightBitColor: Boolean; T: LongInt;
procedure NonIntegralByteRotate; (* вложение *)
{
Эта программа осуществляет поворот изображений с разрешением меньшим, чем 8 бит на пиксел, а имеено: черно-белые (1-бит) и 16-цветные (4-бит) изображения. Имейте в виду, что такие вещи, как 2-битные изображения также могли бы вращаться, но Microsoft не включил данный формат в свои спецификации и не поддерживает его. }
var
X, Y: LongInt; I: LongInt; MaskBits, CurrentBits: Byte; FirstMask, LastMask: Byte; PLastScanLine: PByte; FirstIndex, CurrentBitIndex: LongInt; ShiftRightAmount, ShiftRightStart: LongInt;
begin { Перемещаем PLastScanLine в первую колонку последней линии чередования bmpBufferR. } PLastScanLine := bmpBufferR; (*$IFDEF Win32*) Inc(PLastScanLine, BytesPerScanLineR * (PbmpInfoR^.biWidth - 1) ); (*$ELSE*) Win16Inc( Pointer(PLastScanLine), BytesPerScanLineR * (PbmpInfoR^.biWidth - 1) ); (*$ENDIF*)
{ Устанавливаем индексирование. } FirstIndex := BitsPerByte - BitCount;
{ Устанавливаем битовые маски:
Для черно-белого изображения, LastMask  := 00000001    и FirstMask := 10000000
Для 4-битного изображения, LastMask  := 00001111    и FirstMask := 11110000
Зададим значения CurrentBits и MaskBits, так как мы будем перемещаться по ним: Для черно-белых изображений: 10000000, 01000000, 00100000, 00010000, 00001000, 00000100, 00000010, 00000001 Для 4-битных изображений: 11110000, 00001111
CurrentBitIndex определяет расстояние от крайнего правого бита до позиции CurrentBits. Например, если мы находимся в одиннадцатой колонке черно-белого изображения, CurrentBits равен 11 mod 8 := 3, или 3-й самый левый бит. Таким образом, крайне правый бит должен переместиться на четыре позиции, чтобы попасть на позицию CurrentBits. CurrentBitIndex как раз и хранит такое значение. } LastMask := 1 shl BitCount - 1; FirstMask := LastMask shl FirstIndex;
CurrentBits := FirstMask; CurrentBitIndex := FirstIndex;
ShiftRightStart := BitCount * (PixelsPerByte - 1);
{ Вот мясо. Перебираем в цикле все пиксели и соответственно вращаем. } { Помните что DIB'ы имеют происхождение противоположное DDB'сам. }
{ Счетчик Y указывает на текущую строчку исходного изображения. } for Y := 1 to PbmpInfoR^.biHeight do begin PbmpBufferR := PLastScanLine;
{ Счетчик X указывает на текущую колонку пикселей исходного изображения. Здесь мы имеем дело только с полностью заполненными байтами. Обработка 'частично заполненных' байтов происходит ниже. } for X := 1 to WholeBytes do begin { Выбираем биты, начиная с 10000000 для черно-белых и заканчивая 11110000 для 4-битных изображений. } MaskBits := FirstMask; { ShiftRightAmount - сумма, необходимая для перемещения текущего байта через весь путь (помните, я об этом говорил выше) в правую часть. } ShiftRightAmount := ShiftRightStart; for I := 1 to PixelsPerByte do begin { Вот гарнир. Берем текущий байт вращаемого изображения и маскируем его с not CurrentBits. Гасятся нулями только биты CurrentBits, сам байт перемещается без изменений. Пример: Для черно-белого изображения, если бы мы находились в 11-й колонке (см. пример выше), мы должны нулем погасить 3-й слева бит, то есть мы должны использовать PbmpBufferR^ и 11011111.
Теперь рассмотрим наш текущий исходный байт. Для черно-белых изображений мы организуем цикл с шагом через бит, в общей сложности для восьми пикселей. Для 4-битных изображений мы делаем цикл с обработкой четырех битов за проход для двух пикселей. В любом случае мы делаем это через маскирование с MaskBits ('PbmpBuffer^ и MaskBits'). Теперь нам нужно получить бит(ы) из той колонки(ок), на которую отобразится CurrentBits. Мы это делаем с помощью перемещения их в крайне правую часть байта ('shr ShiftRightAmount'), затем сдвигая их налево с помощью вышеупомянутого CurrentBitIndex ('shl CurrentBitIndex'). Дело в том, что хотя перемещение вправо с параметром -n должно быть просто перемещением налево с параметром +n, в Delphi это не работает. Итак, мы начинаем с первого байта, перемещая пиксели в правую часть насколько это возможно незанятыми позициями.
Наконец, мы имеем наш исходный бит(ы), перемещенный на нужное место с погашенными нулями битами. Последнее делаем непосредственно или с помощью PbmpBufferR^ (гасим биты в CurrentBits, помните?).
Мда... &quotПросто&quot. Ладно, поехали дальше. }
PbmpBufferR^ := ( PbmpBufferR^ and not CurrentBits ) or ( (PbmpBuffer^ and MaskBits) shr ShiftRightAmount shl CurrentBitIndex );
{ Сдвигаем MaskBits для следующей итерации. } MaskBits := MaskBits shr BitCount; (*$IFDEF Win32*) { Перемещаем наш указатель на буфер вращаемого изображения на одну линию чередования. } Dec(PbmpBufferR, BytesPerScanLineR); (*$ELSE*) Win16Dec( Pointer(PbmpBufferR), BytesPerScanLineR ); (*$ENDIF*) { Нам не нужно перемещаться непосредственно вправо в течение некоторого времени. } Dec(ShiftRightAmount, BitCount); end; (*$IFDEF Win32*) Inc(PbmpBuffer); (*$ELSE*) Win16Inc( Pointer(PbmpBuffer), 1 ); (*$ENDIF*) end;
{ Если есть "частично заполненный" байт, самое время о нем позаботиться. } if ExtraPixels <> 0 then begin { Делаем такие же манипуляции, как в цикле выше. } MaskBits := FirstMask; ShiftRightAmount := ShiftRightStart; for I := 1 to ExtraPixels do begin PbmpBufferR^ := ( PbmpBufferR^ and not CurrentBits ) or ( (PbmpBuffer^ and MaskBits) shr ShiftRightAmount shl CurrentBitIndex );
MaskBits := MaskBits shr BitCount; (*$IFDEF Win32*) Dec(PbmpBufferR, BytesPerScanLineR); (*$ELSE*) Win16Dec( Pointer(PbmpBufferR), BytesPerScanLineR ); (*$ENDIF*) Dec(ShiftRightAmount, BitCount); end; (*$IFDEF Win32*) Inc(PbmpBuffer); (*$ELSE*) Win16Inc( Pointer(PbmpBuffer), 1 ); (*$ENDIF*) end;
{ Пропускаем заполнение. } (*$IFDEF Win32*) Inc(PbmpBuffer, PaddingBytes); (*$ELSE*) Win16Inc( Pointer(PbmpBuffer), PaddingBytes ); (*$ENDIF*)
if CurrentBits = LastMask then begin { Мы в конце этого байта. Начинаем с другой колонки. } CurrentBits := FirstMask; CurrentBitIndex := FirstIndex; { Идем вниз колонки вращаемого изображения , но одну колонку пропускаем. } (*$IFDEF Win32*) Inc(PLastScanLine); (*$ELSE*) Win16Inc( Pointer(PLastScanLine), 1 ); (*$ENDIF*) end else begin { Продолжаем заполнять этот байт. } CurrentBits := CurrentBits shr BitCount; Dec(CurrentBitIndex, BitCount); end; end; end; { procedure NonIntegralByteRotate (* вложение *) }

procedure IntegralByteRotate; (* вложение *)
var
X, Y: LongInt; (*$IFNDEF Win32*) I: Integer; (*$ENDIF*)
begin { Перемещаем PbmpBufferR в первую колонку последней линии чередования bmpBufferR. } (*$IFDEF Win32*) Inc( PbmpBufferR, BytesPerScanLineR * (PbmpInfoR^.biWidth - 1) ); (*$ELSE*) Win16Inc( Pointer(PbmpBufferR) , BytesPerScanLineR * (PbmpInfoR^.biWidth - 1) ); (*$ENDIF*)
{ Вот мясо. Перебираем в цикле все пиксели и соответственно вращаем. } { Remember that DIBs have their origins opposite from DDBs. } for Y := 1 to PbmpInfoR^.biHeight do begin for X := 1 to PbmpInfoR^.biWidth do begin { Копируем пиксели. } (*$IFDEF Win32*) Move(PbmpBuffer^, PbmpBufferR^, BytesPerPixel); Inc(PbmpBuffer, BytesPerPixel); Dec(PbmpBufferR, BytesPerScanLineR); (*$ELSE*) for I := 1 to BytesPerPixel do begin PbmpBufferR^ := PbmpBuffer^; Win16Inc( Pointer(PbmpBuffer), 1 ); Win16Inc( Pointer(PbmpBufferR), 1 ); end; Win16Dec( Pointer(PbmpBufferR), BytesPerScanLineR + BytesPerPixel); (*$ENDIF*) end; (*$IFDEF Win32*) { Пропускаем заполнение. } Inc(PbmpBuffer, PaddingBytes); { Идем вверх колонки вращаемого изображения , но одну колонку пропускаем. } Inc(PbmpBufferR, ColumnBytes + BytesPerPixel); (*$ELSE*) Win16Inc( Pointer(PbmpBuffer), PaddingBytes ); Win16Inc( Pointer(PbmpBufferR), ColumnBytes + BytesPerPixel ); (*$ENDIF*) end; end;

{ Это тело процедуры RotateBitmap90DegreesCounterClockwise. }
begin
{ Никогда сами не вызывайте GetDIBSizes! Это испортит ваше изображение. }
MemoryStream := TMemoryStream.Create;
{ Для работы: Прежде всего установим размер. Это устранит перераспределение памяти для MemoryStream. Вызов GetDIBSizes будет к месту, но, как отмечалось выше, это может исказить ваше изображение. Вызов некоторых API функций вероятно позаботился бы об этом, но это тема отдельного разговора. }
{ Недокументированный метод. Все же программист иногда сродни шаману. } ABitmap.SaveToStream(MemoryStream);
{ Don't need you anymore. We'll make a new one when the time comes. } ABitmap.Free;
bmpBuffer := MemoryStream.Memory; { Get the offset bits. This may or may not include palette information. } BitmapOffset := PBitmapFileHeader(bmpBuffer)^.bfOffBits;
{ Устанавливаем PbmpInfoR на указатель информационного заголовка исходного изображения. } { Эти заголовки могут немного раздражать, но они необходимы для работы. } (*$IFDEF Win32*) Inc( bmpBuffer, SizeOf(TBitmapFileHeader) ); (*$ELSE*) Win16Inc( Pointer(bmpBuffer), SizeOf(TBitmapFileHeader) ); (*$ENDIF*) PbmpInfoR := PBitmapInfoHeader(bmpBuffer);
{ Устанавливаем bmpBuffer и PbmpBuffer так, чтобы они указывали на биты оригинального изображения. } bmpBuffer := MemoryStream.Memory; (*$IFDEF Win32*) Inc(bmpBuffer, BitmapOffset); (*$ELSE*) Win16Inc( Pointer(bmpBuffer), BitmapOffset ); (*$ENDIF*) PbmpBuffer := bmpBuffer;
{ Имейте в виду, что нам не нужно беспокоиться о совместимости изображений версии 4 и 3, поскольку области, которые мы используем, а именно -- biWidth, biHeight, и biBitCount -- располагаются на один и тех же местах в обоих структурах. Итак, одной проблемой меньше. Изображения версии OS/2, между прочим, при этом гнусно рушатся. Обидно. } with PbmpInfoR^ do begin { ShowMessage('Компрессия := ' + IntToStr(biCompression)); } BitCount := biBitCount; { ShowMessage('BitCount := ' + IntToStr(BitCount)); }
{ ScanLines - "выровненный" DWORD. } BytesPerScanLine := ((((biWidth * BitCount) + 31) div 32) * SizeOf(DWORD)); BytesPerScanLineR := ((((biHeight * BitCount) + 31) div 32) * SizeOf(DWORD));
AtLeastEightBitColor := BitCount >= BitsPerByte; if AtLeastEightBitColor then begin { Нас не должен волновать бит-тильда. Классно. } BytesPerPixel := biBitCount shr 3; SignificantBytes := biWidth * BitCount shr 3; { Дополнительные байты необходимы для выравнивания DWORD. } PaddingBytes := BytesPerScanLine - SignificantBytes; ColumnBytes := BytesPerScanLineR * biWidth; end else begin { Одно- или четырех-битовое изображение. Уфф. } PixelsPerByte := SizeOf(Byte) * BitsPerByte div BitCount; { Все количество байтов полностью заполняется информацией о пикселе. } WholeBytes := biWidth div PixelsPerByte; { Обрабатываем любые дополнительные биты, которые могут частично заполнять байт. Например, черно-белое изображение, у которого 14 пикселей описываются каждый соответственно своим байтом, плюс одним дополнительным, у которого на самом деле используются 6 битов, остальное мусор. } ExtraPixels := biWidth mod PixelsPerByte; { Все дополнительные байты -- если имеются -- требуется DWORD-выровнять по линии чередования. } PaddingBytes := BytesPerScanLine - WholeBytes; { Если есть дополнительные биты (то есть имеется 'дополнительный байт'), то один из заполненных байтов уже был принят во внимание. } if ExtraPixels <> 0 then Dec(PaddingBytes); end; { if AtLeastEightBitColor then }
{ TMemoryStream, обслуживающий вращаемые биты. } MemoryStreamR := TMemoryStream.Create; { Устанавливаем размер вращаемого изображения. Может отличаться от исходного из-за выравнивания DWORD. } MemoryStreamR.SetSize(BitmapOffset + BytesPerScanLineR * biWidth); end; { with PbmpInfoR^ do }
{ Копируем заголовки исходного изображения. } MemoryStream.Seek(0, soFromBeginning); MemoryStreamR.CopyFrom(MemoryStream, BitmapOffset);
{ Вот буфер, который мы будем "вращать". } bmpBufferR := MemoryStreamR.Memory; { Пропускаем заголовки, yadda yadda yadda... } (*$IFDEF Win32*) Inc(bmpBufferR, BitmapOffset); (*$ELSE*) Win16Inc( Pointer(bmpBufferR), BitmapOffset ); (*$ENDIF*) PbmpBufferR := bmpBufferR;
{ Едем дальше. } if AtLeastEightBitColor then IntegralByteRotate else NonIntegralByteRotate;
{ Удовлетворяемся исходными битами. } MemoryStream.Free;
{ Теперь устанавливаем PbmpInfoR, чтобы он указывал на информационный заголовок вращаемого изображения. } PbmpBufferR := MemoryStreamR.Memory; (*$IFDEF Win32*) Inc( PbmpBufferR, SizeOf(TBitmapFileHeader) ); (*$ELSE*) Win16Inc( Pointer(PbmpBufferR), SizeOf(TBitmapFileHeader) ); (*$ENDIF*) PbmpInfoR := PBitmapInfoHeader(PbmpBufferR);
{ Меняем ширину с высотой в информационном заголовке вращаемого изображения. } with PbmpInfoR^ do begin T := biHeight; biHeight := biWidth; biWidth := T; biSizeImage := 0; end;
ABitmap := TBitmap.Create;
{ Вращение с самого начала. } MemoryStreamR.Seek(0, soFromBeginning); { Загружаем это снова в ABitmap. } ABitmap.LoadFromStream(MemoryStreamR);
MemoryStreamR.Free; end;

procedure RotateBitmap180Degrees(var ABitmap: TBitmap);
var
RotatedBitmap: TBitmap;
begin
RotatedBitmap := TBitmap.Create; with RotatedBitmap do begin Width := ABitmap.Width; Height := ABitmap.Height; Canvas.StretchDraw( Rect(ABitmap.Width, ABitmap.Height, 0, 0), ABitmap ); end; ABitmap.Free; ABitmap := RotatedBitmap; end;

end.

[000122]



При работе программ на Delphi


Nomadic советует:

A: (AB): Залить фон битмапа синим цветом. [001494]



Проблема с классом TBitmap


    uses  Graphics, Classes, WinProcs, SysUtils;

type
TGxBitmap = class( TBitmap )    <<<<<<<<требуется тип класса

В Delphi 2.0 ссылка на WinProcs в действительности является ссылкой на модуль Windows. Модуль Windows практически является комбинацией модулей WinTypes и WinProcs. В нем определен тип с именем TBitmap и, поскольку WinProcs указан после Graphics, тип TBitmap в приведенном примере реально определяется в модуле Windows, а не Graphics.

Это работало в Delphi 1.0, поскольку вы должны были ссылаться на WinTypes для получения декларации TBitmap, но вы не делали этого.

Решение проблемы: в списке uses разместите Graphics после WinProcs.

- Pat Ritchey [000915]



Прозрачная растровая кисть


Следующий модуль выводит на форме два изображения. Одно используется в качестве фона, второе выводится на передний план. Изображение переднего плана отображается как изображение, обладающее "прозрачностью".

Более подробную информацию можно почерпнуть из комментариев.

    { Цель:     Вывод прозрачного изображения, загруженного из файла
Автор:    Michael Vincze (vincze@ti.com) Дата:     04/20/95 Использование: Создайте пустую форму с именем Form1, скомпилируйте и запустите проект. Ограничения: Данный модуль протестирован как с 16-, так и с 256-цветными изображениями. Подразумевается, что левый нижний пиксель изображения содержит цвет, определяющий прозрачность. Примечание: Если этот модуль предполагается использовать для любой цели, пожалуйста, оставьте данный заголовок нетронутым в качестве подтверждения авторских прав. Если вы имеете к.-либо замечания или предложения по улучшению кода, пожалуйста, обратитесь к автору. Никаких ограничений по использованию данного кода не существует. Версия:  1.00  04/20/95  Первый выпуск }
unit Tbmpu;

interface

uses
SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls, Forms, Dialogs, ExtCtrls;
type
TForm1 = class(TForm) procedure FormCreate(Sender: TObject); procedure FormClose(Sender: TObject; var Action: TCloseAction); private { Private declarations } ImageForeGround: TImage; ImageBackGround: TImage; public { Public declarations } end;
procedure DrawTransparentBitmap (ahdc: HDC;
Image: TImage; xStart, yStart: Word);
var
Form1: TForm1;
implementation

{$R *.DFM}

procedure DrawTransparentBitmap (ahdc: HDC;
Image: TImage; xStart, yStart: Word); var
TransparentColor: TColor; cColor          : TColorRef; bmAndBack, bmAndObject, bmAndMem, bmSave, bmBackOld, bmObjectOld, bmMemOld, bmSaveOld       : HBitmap; hdcMem, hdcBack, hdcObject, hdcTemp, hdcSave         : HDC; ptSize          : TPoint; begin
{ устанавливаем цвет прозрачности для левого нижнего пиксела изображения
}
TransparentColor := Image.Picture.Bitmap.Canvas.Pixels[0,
Image.Height - 1]; TransparentColor := TransparentColor or $02000000;

hdcTemp := CreateCompatibleDC (ahdc);
SelectObject (hdcTemp, Image.Picture.Bitmap.Handle); { выбираем изображение }

{ преобразуем размеры изображения к логическим точкам
}
ptSize.x := Image.Width;
ptSize.y := Image.Height;
DPtoLP (hdcTemp, ptSize, 1);  { преобразуем логические точки устройства }

{ создаем некие DCы для хранения временных данных
}
hdcBack   := CreateCompatibleDC(ahdc);
hdcObject := CreateCompatibleDC(ahdc);
hdcMem    := CreateCompatibleDC(ahdc);
hdcSave   := CreateCompatibleDC(ahdc);

{ создаем изображение для каждого DC
}

{ черно-белый DC
}
bmAndBack   := CreateBitmap (ptSize.x, ptSize.y, 1, 1, nil);
bmAndObject := CreateBitmap (ptSize.x, ptSize.y, 1, 1, nil);

bmAndMem    := CreateCompatibleBitmap (ahdc, ptSize.x, ptSize.y);
bmSave      := CreateCompatibleBitmap (ahdc, ptSize.x, ptSize.y);

{ каждый DC должен выбрать bitmap-объект для хранения данных о пикселях
}
bmBackOld   := SelectObject (hdcBack, bmAndBack);
bmObjectOld := SelectObject (hdcObject, bmAndObject);
bmMemOld    := SelectObject (hdcMem, bmAndMem);
bmSaveOld   := SelectObject (hdcSave, bmSave);

{ устанавливаем соответствующих режим управления (Map) памятью
}
SetMapMode (hdcTemp, GetMapMode (ahdc));

{ сохраняем переданное сюда изображение, поскольку оно будет переписано
}
BitBlt (hdcSave, 0, 0, ptSize.x, ptSize.y, hdcTemp, 0, 0, SRCCOPY);

{ устанавливаем цвет фона исходного DC
в цвет прозрачности (чтобы фон был прозрачным) }
cColor := SetBkColor (hdcTemp, TransparentColor);

{ создаем объект-маску для изображения, выполняя BitBlt(),
и делая из исходного изображения черного-белое }
BitBlt (hdcObject, 0, 0, ptSize.x, ptSize.y, hdcTemp, 0, 0, SRCCOPY);

{ устанавливаем цвет фона исходного DC обратно в оригинальный цвет
}
SetBkColor (hdcTemp, cColor);

{ создаем инвертированную маску-объект
}
BitBlt (hdcBack, 0, 0, ptSize.x, ptSize.y, hdcObject, 0, 0, NOTSRCCOPY);

{ копируем фон главного DC в целевой
}
BitBlt (hdcMem, 0, 0, ptSize.x, ptSize.y, ahdc, xStart, yStart, SRCCOPY);

{ маскируем места, где будет располагаться изображение
}
BitBlt (hdcMem, 0, 0, ptSize.x, ptSize.y, hdcObject, 0, 0, SRCAND);

{ маскируем в изображении пиксели с прозрачным цветом
}
BitBlt (hdcTemp, 0, 0, ptSize.x, ptSize.y, hdcBack, 0, 0, SRCAND);

{ делаем XOR изображения с фоном целевого DC
}
BitBlt (hdcMem, 0, 0, ptSize.x, ptSize.y, hdcTemp, 0, 0, SRCPAINT);

{ копируем целевое изображение на экран
}
BitBlt (ahdc, xStart, yStart, ptSize.x, ptSize.y, hdcMem, 0, 0, SRCCOPY);

{ устанавливаем оригинальное изображение обратно, в переданное сюда изображение
}
BitBlt (hdcTemp, 0, 0, ptSize.x, ptSize.y, hdcSave, 0, 0, SRCCOPY);

{ удаляем изображения из памяти
}
DeleteObject (SelectObject (hdcBack, bmBackOld));
DeleteObject (SelectObject (hdcObject, bmObjectOld));
DeleteObject (SelectObject (hdcMem, bmMemOld));
DeleteObject (SelectObject (hdcSave, bmSaveOld));

{ удаляем из памяти DCы
}
DeleteDC (hdcMem);
DeleteDC (hdcBack);
DeleteDC (hdcObject);
DeleteDC (hdcSave);
DeleteDC (hdcTemp);
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
{ создаем для двух изображений элементы управления Image и назначаем им родителей
}
ImageForeGround := TImage.Create (Form1);
ImageForeGround.Parent := Form1;
ImageBackGround := TImage.Create (Form1);
ImageBackGround.Parent := Form1;

{ загружаем изображения
}
ImageBackGround.Picture.LoadFromFile ('c:\delphi\images\splash\16color\earth.bmp');
ImageForeGround.Picture.LoadFromFile ('c:\delphi\images\splash\16color\athena.bmp');

{ устанавливаем размер изображения заднего плана равным размеру исходного изображения
}
with ImageBackGround do
begin Left := 0; Top := 0; Width := Picture.Width; Height := Picture.Height; end;
{ устанавливаем размер изображения переднего плана, отцентрированного по отношению к изображению заднего плана
}
with ImageForeGround do
begin Left := (ImageBackGround.Picture.Width - Picture.Width) div 2; Top := (ImageBackGround.Picture.Height - Picture.Height) div 2; Width := Picture.Width; Height := Picture.Height; end;
{ не выводим прозрачное изображение так, как это делает BitBlt()
функция, пользуемся функцией DrawTransparentBitmap() }
ImageForeGround.Visible := False;

{ рисуем прозрачное изображение
обратите внимание на то, как DC переднего плана используется в расположенной ниже функции }
DrawTransparentBitmap (ImageBackGround.Picture.Bitmap.Canvas.Handle, {HDC}
ImageForeGround, {TImage} ImageForeGround.Left, {X} ImageForeGround.Top {Y} ); end;

procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
{ освобождаем изображения
}
ImageForeGround.Free;
ImageBackGround.Free;
end;

end.

[001946]



Прозрачные формы и изображения


Вот отличный код, рисующий одно изображение с прозрачными областями на другом.

    {Данная процедура рисует исходное изображение на целевом,
получая информацию об областях исходного изображения, которые должны остаться в области целевого изображения, имеющей прозрачный цвет. t   = Целевой холст для рисования x,y = Позиция целевого изображения, где должно быть наложено исходноеs   = Исходное изображение TrCol = Цвет, определяющий прозрачность в исходном изображении Примечание: Не забывайте обновлять (repaint) целевое изображение, например так: Image1.Invalidate}
procedure DrawTransparent(t: TCanvas; x,y: Integer; s: TBitmap; TrCol: TColor);
var
bmpXOR, bmpAND, bmpINVAND, bmpTarget: TBitmap; oldcol: Longint; begin
try
bmpAND := TBitmap.Create; bmpAND.Width := s.Width; bmpAND.Height := s.Height; bmpAND.Monochrome := True; oldcol := SetBkColor(s.Canvas.Handle, ColorToRGB(TrCol)); BitBlt(bmpAND.Canvas.Handle, 0,0,s.Width,s.Height, s.Canvas.Handle, 0,0, SRCCOPY); SetBkColor(s.Canvas.Handle, oldcol);
bmpINVAND := TBitmap.Create; bmpINVAND.Width := s.Width; bmpINVAND.Height := s.Height; bmpINVAND.Monochrome := True; BitBlt(bmpINVAND.Canvas.Handle, 0,0,s.Width,s.Height, bmpAND.Canvas.Handle, 0,0, NOTSRCCOPY);
bmpXOR := TBitmap.Create; bmpXOR.Width := s.Width; bmpXOR.Height := s.Height; BitBlt(bmpXOR.Canvas.Handle, 0,0,s.Width,s.Height, s.Canvas.Handle, 0,0, SRCCOPY); BitBlt(bmpXOR.Canvas.Handle, 0,0,s.Width,s.Height, bmpINVAND.Canvas.Handle, 0,0, SRCAND);
bmpTarget := TBitmap.Create; bmpTarget.Width := s.Width; bmpTarget.Height := s.Height; BitBlt(bmpTarget.Canvas.Handle, 0,0,s.Width,s.Height, t.Handle, x,y, SRCCOPY); BitBlt(bmpTarget.Canvas.Handle, 0,0,s.Width,s.Height, bmpAND.Canvas.Handle, 0,0, SRCAND); BitBlt(bmpTarget.Canvas.Handle, 0,0,s.Width,s.Height, bmpXOR.Canvas.Handle, 0,0, SRCINVERT); BitBlt(t.Handle, x,y,s.Width,s.Height, bmpTarget.Canvas.Handle, 0,0, SRCCOPY); finally bmpXOR.Free; bmpAND.Free; bmpINVAND.Free; bmpTarget.Free; end;{Конец секции TRY} end;

[001948]



Прозрачный Bitmap


Вам необходимо две копии вашего изображения. Маску и само изображение. Маска является ничем иным, как изображением, состоящим из двух цветов. Черного для тех областей, которые вы хотите показать, и белого для прозрачных. Для Windows 3.1 маска изображения может быть черно-белой, и предназначена для определения размеров изображения. В Win95 черно-белая маска ни при каких обстоятельствах не работает, т.к. у нее должна быть та же глубина цветов, что и у самого изображения, которое вы хотите показать.

Изображение, которое вы хотите показать, должно содержать в прозрачных областях значение цвета, равное 0. Метод помещения изображения на экран такой же, как и в DOS. Маска AND экран, изображение OR или XOR с той же областью.

Ниже приведен код Delphi, позволяя сделать вышеописанное с помощью двух TBitmap.

    Canvas.CopyMode := cmSrcAnd; Canvas.CopyRect(TitleRect, BMask.Canvas, TitleRect); {заполняем "пробелы" изображением} Canvas.CopyMode := cmSrcPaint; Canvas.CopyRect(TitleRect, BTitle.Canvas, TitleRect);

[001800]



Растягивание и отображение изображения


...размер изображения, которое вы назначили свойству picture.graphic, также имеет свойство размера. Если вы зададите элементу управления размер, скажем, 320x200, а хранящемуся в нем растру 160x100, то при сброшенном свойстве stretch вы обнаружите вокруг растра серую полоску.

Вот способ обойти эту проблему.

    procedure TForm.ZoomImage;
var Bitmap: TBitmap;
DstRect: TRect; begin
{ Здесь можно установить новое растровое изображение с нужными для масштабирования пропорциями, таким образом добиваясь заполнения родительсткой области. }
Bitmap := TBitmap.Create; Bitmap.Width := { задайте здесь ширину для измененого масштаба } Bitmap.Height := { задайте здесь высоту для измененого масштаба }
Bitmap.Canvas.StretchDraw(Bitmap.Canvas.ClipRect,{здесь включите ваше изображение, которое вы хотите здесь видеть с измененным масштабом});
{ Здесь назначьте (assign) новое изображение свойству graphic компонента ImageBox. ImageBox автоматически избавляется от ресурсов, использованных с предыдущим изображением. }
Image1.Picture.Graphic := Bitmap; Image1.Invalidate; end;

Это должно работать, или по крайней мере дать вам идею отностительно того, как это можно сделать. Если я что-то забыл, пожалуйста, не судите строго, и скажите мне об этом. Имейте в виду, что все, что я описал выше, не нужно делать в обработчике события onPaint. Мне нравится компонент ImageBox, поскольку у него хватает ума освобождаться от старого изображения при назначении нового. Я предполагаю, что если вы используете метод Assign вместо метода назначения bitmap, то вы просто получите его новую копию, поэтому вместо этого просто воспользуйтесь ImageBox... В конце процедуры вы должны все-таки освободить новое изображение.

Решение проблемы кроется в изменении свойств TImage.Picture.Bitmap.Width и TImage.Picture.Bitmap.Height. [001938]



Растягивание иконки


При выводе объекта TIcon я пытаюсь использовать функцию StretchDraw, но все что у меня получается - это оригинальный размер, вне зависимости от заданной области Trect.

Andrew

StretchDraw не работает с иконками. В данной ситуации я бы поступил так: рисовал бы иконку в Timage и затем назначал изображение другому, большему Timage.

Пример кода:

    procedure TForm1.StringGrid1Click(Sender: TObject);
begin
Image1.Canvas.FillRect(Image1.Canvas.ClipRect); Image1.Canvas.Draw(0, 0, TIcon(StringGrid1.Objects [StringGrid1.Col, StringGrid1.Row])); Form2.Image1.Picture := Image1.Picture; end;
{Примечание. Form2.Image1 имеет Stretch установленный в True и размер, бОльший размера иконки в 4 раза}

Надеюсь я помог вам.

Bill

Дополнение

Андрей Бреслав пишет:

предложенный способ не работает, ибо компонента TImage использует тот же метод StretchDraw, что и спрашивающий. Растянуть иконку можно так:

    procedure TForm1.Button1Click(Sender: TObject);
var
Bmp: TBitMap; begin
Bmp:= TBitMap.Create; Bmp.Height:= GetSystemMetrics(SM_CYICON); Bmp.Width:= GetSystemMetrics(SM_CXICON); Bmp.Canvas.Draw(0,0, Image1.Picture.Icon); Image1.Picture.Bitmap:= Bmp; Bmp.Free; end;

Есть более человечный способ, чем просто рисовать в Image: функция DrawIconEx Win32 API:

    procedure TForm1.Button1Click(Sender: TObject);
begin
DrawIconEx(Canvas.Handle, 5, 5, LoadIcon(0, IDI_APPLICATION), 16, 32, 0, 0, DI_NORMAL);
end;

Кстати, думаю, людям будет полезно знать по подробнее о DrawIconEx: Рисует иконку или курсор в соответствии с заданными занчениями.

    function DrawIconEx(hdc: HDC; xLeft, yTop: Integer; hIcon: HICON;
cxWidth, cyWidth: Integer; istepIfAniCur: UINT; hbrFlickerFreeDraw: HBRUSH; diFlags: UINT): BOOL; stdcall;
hdc - контекст устройства (TCanvas.Handle) xLeft, yTop - координаты левого верхнего угла hIcon - дескриптор объекта Windows - Icon cxWidth, cyWidth - размеры istepIfAniCur - (!) номер отображаемого кадра в анимированном курсоре hbrFlickerFreeDraw - кисть diFlags - сумма след. занчений: DI_COMPAT - буду благодарен, если объясните DI_DEFAULTSIZE - если cxWidth, cyWidth равны 0, рисует в default размере DI_IMAGE - применяет одну часть кисти DI_MASK - применяет другую часть кисти DI_NORMAL = DI_IMAGE and DI_MASK - применяет обе части кисти [000598]



Рисование без мерцания


...вот я и удивляюсь - почему я получаю мерцание, если я вызываю Repaint или Refresh, а не метод OnPaint напрямую? Или это просто "вариация на тему"?

Имеются две фазы обновления окна. В первой фазе, при выводе окна, Windows посылает ему сообщение WM_ERASEBKGND, сообщающее о необходимости стирания фона перед процедурой рисования. Затем посылается сообщение WM_PAINT, служащее сигналом для закрашивания "переднего плана".

Тем не менее, вы можете пропустить первую фазу, которая вызывает мерцание, одним из двух способов: первый способ заключается в том, что вы форсируете обновление сами, с помощью вызова функции Windows API InvalidateRect. На входе он получает дескриптор окна, указатель на закрашиваемую область - передаем NIL, если вы хотите отрисовать всю область окна - и третий параметр, сообщающий о необходимости очистки фона. Вот как раз последний параметр и должен содержать значение FALSE, если вы сами будете в методе Paint полностью отрисовывать всю область:

    InvalidateRect( Handle, NIL, FALSE ) ;

Handle должен быть дескриптором формы или элемента управления.

Описав первый способ, я скажу, что существует другое подходящее решение - использовать функциональность VCL. Вы можете указать VCL не стирать фон, добавляя [ csOpaque ] к значению свойства ControlStyle, как показано ниже:

    ControlStyle := ControlStyle + [ csOpaque ] ;

Это ограничивает заполнение заднего фона, но вы все еще можете видеть процесс "наполнения" области изображением, т.е. процесс рисования. В этом случае вы можете отделаться от эффекта мельтешения, рисуя на TBitmap и выводя его затем на экран командой CopyRect.

Если вы хотите углубиться в тему дальше, то я отошлю вас к моей статье "Optimizing Display Updates in Delphi" (Оптимизация обновления экрана в Delphi), опубликованной в первом выпуске журнала "Delphi magazine".

Mike Scott. [000684]



Рисование фрактальных графов


Здравствуйте, Валентин!

...Очередная нетленка, которую я предлагаю Вам, написана мной самостоятельно (идею и примеры, реализованные в программе, я нашел в апрельском номере журнала "Химия и жизнь" за 1995 год). Теоретически она производит трансляцию L-систем с выводом образовавшихся фрактальных графов, а практически рисует кусты и деревья. Вроде бесполезно, но очень красиво. Эта программа написана для TP7, хотя легко переносится на Delphi (как то я уже переводил ее, но модуль бесследно исчез). Буду надеяться, что она придется Вам по душе.

    Uses graph, crt;
Const
GrafType = 1; {1..3} Type
PointPtr = ^Point; Point = Record X, Y : Word; Angle : Real; Next : PointPtr End; GrfLine = Array [0..5000] Of Byte; ChangeType = Array [1..30] Of Record Mean : Char; NewString : String End; Var
K, T, Dx, Dy, StepLength, GrafLength : Word; grDriver, Xt : Integer; grMode : Integer; ErrCode : Integer; CurPosition : Point; Descript : GrfLine; StartLine : String Absolute Descript; ChangeNumber, Generation : Byte; Changes : ChangeType; AngleStep : Real; Mem : Pointer;
Procedure Replace (Var Stroka : GrfLine;
OldChar : Char; Repl : String); Var I, J : Word;
Begin
If
(GrafLength = 0) Or (Length (Repl) = 0) Then Exit; I := 1; While I <= GrafLength Do Begin If Chr (Stroka [I]) = OldChar Then Begin For J := GrafLength DownTo I + 1 Do Stroka [J + Length (Repl) - 1] := Stroka [J]; For J := 1 To Length (Repl) Do Stroka [I + J - 1] := Ord (Repl [J]); I := I + J; GrafLength := GrafLength + Length (Repl) - 1; continue End; I := I + 1 End End;

Procedure PushCoord (Var Ptr : PointPtr;
C : Point); Var
P : PointPtr; Begin
New (P); P^.X := C.X; P^.Y := C.Y; P^.Angle := C.Angle; P^.Next := Ptr; Ptr := P End;

Procedure PopCoord (Var Ptr : PointPtr;
Var Res : Point); Begin
If
Ptr <> Nil Then Begin Res.X := Ptr^.X; Res.Y := Ptr^.Y; Res.Angle := Ptr^.Angle; Ptr := Ptr^.Next End End;

Procedure FindGrafCoord (Var Dx, Dy : Word;
Angle : Real; StepLength : Word); Begin
Dx := Round (Sin (Angle) * StepLength * GetMaxX / GetMaxY); Dy := Round ( - Cos (Angle) * StepLength); End;

Procedure NewAngle (Way : ShortInt;
Var Angle : Real; AngleStep : Real); Begin
If
Way >= 0 Then Angle := Angle + AngleStep Else Angle := Angle - AngleStep; If Angle >= 4 * Pi Then Angle := Angle - 4 * Pi; If Angle < 0 Then Angle := 4 * Pi + Angle End;

Procedure Rost (Var Descr : GrfLine;
Cn : Byte; Ch : ChangeType); Var I : Byte;
Begin
For
I := 1 To Cn Do Replace (Descr, Ch [I] .Mean, Ch [I] .NewString); End;

Procedure Init1;
Begin
AngleStep := Pi / 8; StepLength := 7; Generation := 4; ChangeNumber := 1; CurPosition.Next := Nil; StartLine := 'F'; GrafLength := Length (StartLine); With Changes [1] Do Begin Mean := 'F'; NewString := 'FF+[+F-F-F]-[-F+F+F]' End; End;

Procedure Init2;
Begin
AngleStep := Pi / 4; StepLength := 3; Generation := 5; ChangeNumber := 2; CurPosition.Next := Nil; StartLine := 'G'; GrafLength := Length (StartLine); With Changes [1] Do Begin Mean := 'G'; NewString := 'GFX[+G][-G]' End; With Changes [2] Do Begin Mean := 'X'; NewString := 'X[-FFF][+FFF]FX' End; End;

Procedure Init3;
Begin
AngleStep := Pi / 10; StepLength := 9; Generation := 5; ChangeNumber := 5; CurPosition.Next := Nil; StartLine := 'SLFF'; GrafLength := Length (StartLine); With Changes [1] Do Begin Mean := 'S'; NewString := '[+++G][---G]TS' End; With Changes [2] Do Begin Mean := 'G'; NewString := '+H[-G]L' End; With Changes [3] Do Begin Mean := 'H'; NewString := '-G[+H]L' End; With Changes [4] Do Begin Mean := 'T'; NewString := 'TL' End; With Changes [5] Do Begin Mean := 'L'; NewString := '[-FFF][+FFF]F' End; End;

Begin
Case
GrafType Of 1 : Init1; 2 : Init2; 3 : Init3; Else End; grDriver := detect; InitGraph (grDriver, grMode, ''); ErrCode := GraphResult; If ErrCode <> grOk Then Begin WriteLn ('Graphics error:', GraphErrorMsg (ErrCode) ); Halt (1) End; With CurPosition Do Begin X := GetMaxX Div 2; Y := GetMaxY; Angle := 0; MoveTo (X, Y) End; SetColor (white); For K := 1 To Generation Do Begin Rost (Descript, ChangeNumber, Changes); Mark (Mem); For T := 1 To GrafLength Do Begin Case Chr (Descript [T]) Of 'F' : Begin FindGrafCoord (Dx, Dy, CurPosition.Angle, StepLength); With CurPosition Do Begin Xt := X + Dx; If Xt < 0 Then X := 0 Else X := Xt; If X > GetMaxX Then X := GetMaxX; Xt := Y + Dy; If Xt < 0 Then Y := 0 Else Y := Xt; If Y > GetMaxY Then Y := GetMaxY; LineTo (X, Y) End End; 'f' : Begin FindGrafCoord (Dx, Dy, CurPosition.Angle, StepLength); With CurPosition Do Begin Xt := X + Dx; If Xt < 0 Then X := 0 Else X := Xt; If X > GetMaxX Then X := GetMaxX; Xt := Y + Dy; If Xt < 0 Then Y := 0 Else Y := Xt; If Y > GetMaxY Then Y := GetMaxY; MoveTo (X, Y) End End; '+' : NewAngle (1, CurPosition.Angle, AngleStep); '-' : NewAngle ( - 1, CurPosition.Angle, AngleStep); 'I' : NewAngle (1, CurPosition.Angle, 2 * Pi); '[' : PushCoord (CurPosition.Next, CurPosition); ']' : Begin PopCoord (CurPosition.Next, CurPosition); With CurPosition Do MoveTo (X, Y) End End End; Dispose (Mem); Delay (1000) End; Repeat Until KeyPressed; CloseGraph End.

С наилучшими пожеланиями,
Михаил Марковский
mrkvsky@chem.kubsu.ru [000469]



Рисование графов


...вы могли бы использовать объект TCanvas, чем рисовать самому. В вашем случае сгодится компонент TImage, он имеет bitmap и свойство canvas, на котором очень удобно рисовать.

Пример: (Создайте новую форму, добавьте к ней Image и Button. Добавьте следующий код к обработчику события нажатия кнопки)

    var
x,l: Integer; y,a: Double; begin
Image1.Picture.Bitmap := TBitmap.Create; Image1.Picture.Bitmap.Width := Image1.Width; Image1.Picture.Bitmap.Height := Image1.Height; {Эти три строчки могут быть размещены в обработчике Form1.Create} l := Image1.Picture.Bitmap.Width; for x := 0 to l do begin a := (x/l) * 2 * Pi;  {Преобразуем позицию по оси X к углу между 0 & 2Pi} y := Sin(a); {Ваша функция должна находиться здесь} y := y * (Image1.Picture.Bitmap.Height / 2); {Масштабируем по оси Y} y := y * -1; {Инвертируем Y, верх экрана это 0 !} y := y + (Image1.Picture.Bitmap.Height / 2); {Добавляем компенсацию для среднего 0} Image1.Picture.Bitmap.Canvas.Pixels[Trunc(x), Trunc(y)] := clBlack; end; end;

Я обнаружил, что лучшим решением будет рисование на холсте. Предпочтительно делать это в отдельной процедуре, которая принимает в качестве параметров TCanvas и TRect. Таким способом мы может передать в качестве параметров холст вашего окна и клиентскую область для рисования на экране, и холст принтера и область клиента для ее позиционирования и печати. Чтобы посмотреть доступные для рисования подпрограммы, взгляните на методы холста.

[001809]



Рисование изображения на форме


    unit Unit1;

interface

uses
SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls, Forms, Dialogs;
type
TForm1 = class(TForm) procedure FormCreate(Sender: TObject); procedure FormPaint(Sender: TObject); private { Private declarations } public { Public declarations } end;
var
Form1: TForm1; Bitmap: TBitmap;
implementation

{$R *.DFM}

procedure TForm1.FormCreate(Sender: TObject);
begin
Bitmap := TBitmap.Create; Bitmap.LoadFromFile('C:\WINDOWS\cars.BMP'); end;

procedure TForm1.FormPaint(Sender: TObject);
var
X, Y, W, H: LongInt; begin
with Bitmap do begin W := Width; H := Height; end; Y := 0; while Y < Height do begin X := 0; while X < Width do begin Canvas.Draw(X, Y, Bitmap); Inc(X, W); end; Inc(Y, H); end; end;

end.

[001941]



*** Рисование КРИВЫХ в Delphi? ***


Решение 1

Автор: dmitrys@phyast.la.asu.edu (Dmitry Streblechenko)

В: У кого-нибудь есть исходный код или какая-либо информация для рисования кривых Безье? Я должен использовать их в своем компоненте. Пожалуйста используйте для ответа мой адрес электронной почты.

О: Я делал это недавно; мне было лениво разбираться с тем, как рисовать кривые Безье с помощью Win API, поэтому я использовал функцию Polyline().

Примечание: для координатных точек я использовал реальные величины типа floating (я применял некоторый тип виртуального экрана), округляя их до целого.

    PBezierPoint = ^TBezierPoint; TBezierPoint = record X,Y:double;   //основной узел Xl,Yl:double; //левая контрольная точка Xr,Yr:double; //правая контрольная точка end;
//P1 и P2 - две точки TBezierPoint, расположенные между 0 и 1:
//когда t=0 X=P1.X, Y=P1.Y; когда t=1 X=P2.X, Y=P2.Y;

procedure BezierValue(P1,P2:TBezierPoint; t:double; var X,Y:double);
var t_sq,t_cb,r1,r2,r3,r4:double; begin t_sq := t * t; t_cb := t * t_sq; r1 := (1 - 3*t + 3*t_sq -   t_cb)*P1.X; r2 := (    3*t - 6*t_sq + 3*t_cb)*P1.Xr; r3 := (          3*t_sq - 3*t_cb)*P2.Xl; r4 := (                     t_cb)*P2.X; X  := r1 + r2 + r3 + r4; r1 := (1 - 3*t + 3*t_sq -   t_cb)*P1.Y; r2 := (    3*t - 6*t_sq + 3*t_cb)*P1.Yr; r3 := (          3*t_sq - 3*t_cb)*P2.Yl; r4 := (                     t_cb)*P2.Y; Y  := r1 + r2 + r3 + r4; end;

Для рисования кривой Безье разделяем интервал между P1 и P2 на несколько отрезков (их количество влияет на точность воспроизведения кривой, 3 - 4 точки вполне достаточно), затем в цикле создаем массив точек, используем описанную выше процедуру с параметром t от 0 до 1 и рисуем данный массив точек, используя функцию polyline().

Решение 2

В: У кого-нибудь есть исходный код или какая-либо информация для рисования кривых Безье? Я должен использовать их в своем компоненте. Пожалуйста используйте для ответа мой адрес электронной почты.

Я решил ответить на этот крик души - причина?: 1. Не первый раз вижу подобный вопрос, 2. Задача настолько избита, что я без труда нашел ответ в своем архиве. (BTW: У меня есть более старые решения, чем это ;-P)

Тем не менее эта технология жива до сих пор и приносит свои плоды:

    (********************************************************************)
(*                         GRAPHIX TOOLBOX 4.0                      *)
(*       Copyright (c) 1985, 87 by  Borland International, Inc.     *)
(********************************************************************)
unit GShell;

interface

{------------------------------ вырезано --------------------------}

procedure Bezier(A : PlotArray; MaxContrPoints : integer;
var B : PlotArray; MaxIntPoints : integer);
implementation

{------------------------------ вырезано --------------------------}

procedure Bezier{(A : PlotArray; MaxContrPoints : integer;
var B : PlotArray; MaxIntPoints : integer)}; const
MaxControlPoints = 25; type
CombiArray = array[0..MaxControlPoints] of Float; var
N : integer; ContrPoint, IntPoint : integer; T, SumX, SumY, Prod, DeltaT, Quot : Float; Combi : CombiArray;
begin
MaxContrPoints := MaxContrPoints - 1; DeltaT := 1.0 / (MaxIntPoints - 1); Combi[0] := 1; Combi[MaxContrPoints] := 1; for N := 0 to MaxContrPoints - 2 do Combi[N + 1] := Combi[N] * (MaxContrPoints - N) / (N + 1); for IntPoint := 1 to MaxIntPoints do begin T := (IntPoint - 1) * DeltaT; if T <= 0.5 then begin Prod := 1.0 - T; Quot := Prod; for N := 1 to MaxContrPoints - 1 do Prod := Prod * Quot; Quot := T / Quot; SumX := A[MaxContrPoints + 1, 1]; SumY := A[MaxContrPoints + 1, 2]; for N := MaxContrPoints downto 1 do begin SumX := Combi[N - 1] * A[N, 1] + Quot * SumX; SumY := Combi[N - 1] * A[N, 2] + Quot * SumY; end; end else begin Prod := T; Quot := Prod; for N := 1 to MaxContrPoints - 1 do Prod := Prod * Quot; Quot := (1 - T) / Quot; SumX := A[1, 1]; SumY := A[1, 2]; for N := 1 to MaxContrPoints do begin SumX := Combi[N] * A[N + 1, 1] + Quot * SumX; SumY := Combi[N] * A[N + 1, 2] + Quot * SumY; end; end; B[IntPoint, 1] := SumX * Prod; B[IntPoint, 2] := SumY * Prod; end; end; { Bezier }

end. { GShell }

[000110]



Создание DIB из BMP


Если файл хранится в формате BMP, как мне преобразовать его в DIB и как затем отобразить?

Это не тривиально, но помочь нам смогут функции GetDIBSizes и GetDIB из модуля GRAPHICS.PAS. Приведу две процедуры: одну для создания DIB из TBitmap и вторую для его освобождения:

    { Преобразование TBitmap в DIB }
procedure BitmapToDIB( Bitmap : TBitmap ; var BitmapInfo : PBitmapInfo ; var InfoSize   : integer ; var Bits       : pointer ; var BitsSize   : longint ) ;
begin BitmapInfo := NIL ; InfoSize := 0 ; Bits := NIL ; BitsSize := 0 ; if not Bitmap.Empty then try GetDIBSizes( Bitmap.Handle, InfoSize, BitsSize ) ; GetMem( BitmapInfo, InfoSize ) ; Bits := GlobalAllocPtr( GMEM_MOVEABLE, BitsSize ) ; if Bits = NIL then Raise EOutOfMemory.Create( 'Не хватает памяти для пикселей изображения' ) ; if not GetDIB( Bitmap.Handle, Bitmap.Palette, BitmapInfo^, Bits^ ) then Raise Exception.Create( 'Не могу создать DIB' ) ; except if BitmapInfo <> NIL then FreeMem( BitmapInfo, InfoSize ) ; if Bits <> NIL then GlobalFreePtr( Bits ) ; BitmapInfo := NIL ; Bits := NIL ; Raise ; end ; end ;

{ используйте FreeDIB для освобождения информации об изображении и битовых указателей }
procedure FreeDIB( BitmapInfo : PBitmapInfo ; InfoSize   : integer ; Bits       : pointer ; BitsSize   : longint ) ;
begin if BitmapInfo <> NIL then FreeMem( BitmapInfo, InfoSize ) ; if Bits <> NIL then GlobalFreePtr( Bits ) ; end ;

Создаем форму с TImage Image1 и загружаем в него 256-цветное изображение, затем рядом размещаем TPaintBox. Добавляем следующие строчки к private-объявлениям вашей формы:

    { Private declarations } BitmapInfo : PBitmapInfo ; InfoSize   : integer ; Bits       : pointer ; BitsSize   : longint ;

Создаем нижеприведенные обработчики событий, которые демонстрируют процесс отрисовки DIB:

    procedure TForm1.FormCreate(Sender: TObject); begin BitmapToDIB( Image1.Picture.Bitmap, BitmapInfo, InfoSize, Bits, BitsSize ) ; end;
procedure TForm1.FormDestroy(Sender: TObject); begin FreeDIB( BitmapInfo, InfoSize, Bits, BitsSize ) ; end;
procedure TForm1.PaintBox1Paint(Sender: TObject); var OldPalette : HPalette ; begin if Assigned( BitmapInfo ) and Assigned( Bits ) then with BitmapInfo^.bmiHeader, PaintBox1.Canvas do begin OldPalette := SelectPalette( Handle, Image1.Picture.Bitmap.Palette, false ) ; try RealizePalette( Handle ) ; StretchDIBits( Handle, 0, 0, PaintBox1.Width, PaintBox1.Height, 0, 0, biWidth, biHeight, Bits, BitmapInfo^, DIB_RGB_COLORS, SRCCOPY ) ; finally SelectPalette( Handle, OldPalette, true ) ; end ; end ; end;

Это поможет вам сделать первый шаг. Единственное, что вы можете захотеть, это создание собственного HPalette на основе DIB, вместо использования TBitmap и своей палитры. Функция с именем PaletteFromW3DIB из GRAPHICS.PAS как раз этим и занимается, но она не объявлена в качестве экспортируемой, поэтому для ее использования необходимо скопировать ее исходный код и вставить его в ваш модуль.

- Mike Scott [000785]



Создание и использование 256-цветной палитры


Вот пример того, как можно создать и использовать палитру для 256-цветных изображений. Вам, вероятно, необходимо использовать API функции SelectPalette и RealizePalette, в зависимости от того как вы хотите использовать ваше изображение.

    procedure TfrmMain.MakePalette(forBitMap: TBitMap);
var
pNewPal : PLogPalette; lSize   : LongInt; nCntr   : Byte; begin
lSize := SizeOf(TLogPalette) + SizeOf(TPaletteEntry) * 256; try GetMem(pNewPal, lSize); pNewPal^.palNumEntries := 256; pNewPal^.palVersion := $300; {$R-}{выключаем контроль допустимого диапазона} {создаем данные палитры...} for nCntr := 0 to 254 do begin pNewPal^.palPalEntry[nCntr].peRed   := nCntr + 20; pNewPal^.palPalEntry[nCntr].peGreen := nCntr + 20; pNewPal^.palPalEntry[nCntr].peBlue  := nCntr + 20; pNewPal^.palPalEntry[nCntr].peFlags := pc_nocollapse; end; {$R+}{включаем контроль допустимого диапазона} {удаляем старый hPal; предохраняемся от утечки памяти} DeleteObject(hPal); {создаем новую палитру на основе новых значений} hPal := CreatePalette(pNewPal^); {назначаем новую палитру} forBitMap.Palette := hPal; finally FreeMem(pNewPal, lSize); end; end;

- Bob Teller [000826]



TCanvas и освобождение дескрипторов


TCanvas автоматически ReleaseDC не вызывает. При создании холста с WindowDC в качестве дескриптора, лучшей идеей будет создание потомка TCanvas (моделированного с TControlCanvas):

    type TWindowCanvas = class(TCanvas) private FWinControl: TWinControl; FDeviceContext: HDC; procedure SetWinControl(AControl: TWinControl); protected procedure CreateHandle; override; public destructor Destroy; override; procedure FreeHandle; property WinControl: TWinControl read FWinControl write SetWinControl; end;
implementation
destructor
TWindowCanvas.Destroy; begin FreeHandle; inherited Destroy; end;
procedure TWindowCanvas.CreateHandle; begin if FControl = nil then inherited CreateHandle else begin if FDeviceContext = 0 then FDeviceContext := GetWindowDC(WinControl.Handle); Handle := FDeviceContext; end; end;
procedure TControlCanvas.FreeHandle; begin if FDeviceContext <> 0 then begin Handle := 0; ReleaseDC(WinControl.Handle, FDeviceContext); FDeviceContext := 0; end; end;
procedure TControlCanvas.SetWinControl(AControl: TWinControl); begin if FWinControl <> AControl then begin FreeHandle; FWinControl := AControl; end; end;

Очевидно, вы должны должны следить за ситуацией, и разрушать TWindowCanvas (или освобождать дескриптор) перед тем, как уничтожить элемент управления, связанный с ним. Также, имейте в виду, что дескриптор DeviceContext не освобождается автоматически после обработки каждого сообщения (как это происходит с дескрипторами TControlCanvas); для освобождения дескриптора вы должны явно вызвать FreeHandle (или разрушить Canvas). И, наконец, имейте в виду, что "WindowCanvas.Handle:= 0" не освобождает десктиптор, для его освобождения вы должны вызывать FreeHandle. [001576]



TImage - эффект плавного перехода


...существует ли для этого эффекта какой-либо алгоритм генерации изображений вместо использования кисточки?

Я был скептически настроен к механизму использования кистей, чтобы получить что-либо похожее на эффект перехода/ухода ("fade") по сравнению со стеркой ("wipe"), но вчера вечером я нашел следующее решение, которое делает невероятное - осуществляет плавный переход от одного изображения к другому:

    procedure WaitAWhile(n:longint); var StartTime:longint; begin StartTime:=timeGetTime; While timeGetTime<StartTime+n do; end;
procedure TForm1.Image1Click(Sender: TObject); var BrushBmp,BufferBmp,Buffer2Bmp,ImageBmp,Image2Bmp:TBitmap; j,k,row,col:longint; begin row:=0;col:=0; BrushBmp:=TBitmap.Create; with BrushBmp do begin Monochrome:=false; Width:=8; Height:=8; end; imageBmp:=TBitmap.create; imagebmp.loadfromfile('c:\huh.bmp'); image2bmp:=TBitmap.Create; image2bmp.LoadFromFile('c:\whatsis.bmp'); {При 256 цветах лучше иметь ту же самую палитру!} BufferBmp:=TBitmap.Create; with BufferBmp do begin Height:=200; Width:=200; canvas.brush.bitmap:=TBitmap.Create; end; Buffer2Bmp:=TBitmap.Create; with Buffer2Bmp do begin Height:=200; Width:=200; canvas.brush.bitmap:=TBitmap.Create; end; for k:= 1 to 16 do begin  WaitAWhile(0); {Для пентиума необходимо добавить задержку} for j:=0 to 3 do begin row:=(row+5) mod 8; col:=(col+1) mod 8; if row=0 then   col:=(col+1) mod 8; BrushBmp.canvas.Pixels[row,col]:=clBlack; end; with BufferBmp do begin canvas.copymode:=cmSrcCopy; canvas.brush.bitmap.free; canvas.brush.style:=bsClear; canvas.brush.bitmap:=TBitmap.Create; canvas.brush.bitmap.Assign(BrushBmp); canvas.Rectangle(0,0,200,200); canvas.CopyMode:=cmMergeCopy; canvas.copyrect(rect(0,0,200,200),imageBmp.canvas, rect(0,0,200,200)); end; with Buffer2Bmp do begin canvas.copymode:=cmSrcCopy; canvas.brush.bitmap.free; canvas.brush.style:=bsClear; canvas.brush.bitmap:=TBitmap.Create; canvas.brush.bitmap.Assign(BrushBmp); canvas.Rectangle(0,0,200,200); canvas.copymode:=cmSrcErase; canvas.copyrect(rect(0,0,200,200),image2bmp.canvas, rect(0,0,200,200)); end; BufferBmp.Canvas.CopyMode:=cmSrcPaint; BufferBmp.Canvas.Copyrect(rect(0,0,200,200), Buffer2Bmp.Canvas,rect(0,0,200,200)); canvas.copymode:=cmSrcCopy; canvas.copyrect(rect(0,0,200,200),BufferBmp.Canvas, rect(0,0,200,200)); end;
BufferBmp.canvas.brush.bitmap.free; Buffer2Bmp.canvas.brush.bitmap.free; BrushBmp.Free; BufferBmp.Free; Buffer2Bmp.Free; ImageBmp.Free; image2Bmp.Free; end;

Комментарии: На Pentium I я реально использую 64 кисточки, изменив приведенные выше строки на следующие:

    for k:= 1 to 64 do begin  WaitAWhile(50); for j:=0 to 0 do

; При организации указанной задержки возможно получение плавного перехода.

Заполняя кисть в другом порядке, вы можете получить ряд других эффектов, но приведенная выше версия единственная, которую мне удалось получить максимально похожей на эффект перехода, но вы можете, скажем, написать:

    begin row:=(row+1) mod 8; (*col:=(col+1) mod 8;*) if row=0 then   col:=(col+1) mod 8;

и получить своего рода эффект перехода типа "venetian-blind wipe" (дословно - стерка венецианского хрусталя).

Вопрос: Я чуствую, что я делаю что-то неправильно, существует какая-то хитрость с кистью. Мне нужно все четыре строчки:

    canvas.brush.bitmap.free; canvas.brush.style:=bsClear; canvas.brush.bitmap:=TBitmap.Create; canvas.brush.bitmap.Assign(BrushBmp);

чтобы все работало правильно; но я совсем не понимаю, почему первые три строки являются обязательными, но если я их выкидываю, Assign сработывает только один раз(!?!?!). Это реально работает? Есть способ другого быстрого назначения brush.bitmaps? (В документации в качестве примера указано на Brush.Bitmap.LoadFromFile, но должно быть лучшее решение. Хорошо, допустим приведенный способ лучший, но он кажется неправильным...)

- David C. Ullrich [000786]



Вертикальный текст


    var
Hfont: Thandle;
logfont:TLogFont;
font: Thandle;
count: integer;
begin
LogFont.lfheight:=30; logfont.lfwidth:=10; logfont.lfweight:=900; LogFont.lfEscapement:=-200; logfont.lfcharset:=1; logfont.lfoutprecision:=out_tt_precis; logfont.lfquality:=draft_quality; logfont.lfpitchandfamily:=FF_Modern;
font:=createfontindirect(logfont);
Selectobject(Form1.canvas.handle,font);
SetTextColor(Form1.canvas.handle,rgb(0,0,200)); SetBKmode(Form1.canvas.handle,transparent);
{textout(form1.canvas.handle,10,10,'Повернутый',7);}
for count:=1 to 100 do begin canvas.textout(Random(form1.width),Random(form1.height),'Повернутый');
SetTextColor(form1.canvas.handle,rgb(Random(255),Random(255),Random(255)));
end;
deleteobject(font);

end;

[001619]



Вращение изображения


Вот быстрый и примитивный способ вращения изображения. Должно работать. По крайней мере хоть какой-то выход из-положения, поскольку Windows этого делать не умеет. Но сначала попробуйте на небольший изображениях.

    procedure RotateRight(BitMap : tImage); var FirstC, LastC, c, r : integer;
procedure FixPixels(c,r : integer); var SavePix, SavePix2 : tColor; i, NewC, NewR : integer; begin SavePix := Bitmap.Canvas.Pixels[c,r]; for i := 1 to 4 do begin newc := BitMap.Height-r+1; newr := c; SavePix2 := BitMap.Canvas.Pixels[newc,newr]; Bitmap.Canvas.Pixels[newc,newr] := SavePix; SavePix := SavePix2; c := Newc; r := NewR; end; end;
begin if BitMap.Width <> BitMap.Height then exit; BitMap.Visible := false; with Bitmap.Canvas do begin firstc := 0; lastc := BitMap.Width; for r := 0 to BitMap.Height div 2 do begin for c := firstc to lastc do begin FixPixels(c,r); end; inc(FirstC); Dec(LastC); end; end; BitMap.Visible := true; end;

-Mike Williams [000715]



Вращение изображения II


...я думаю над принудительным грубым методом, но его эффективность может быть сомнительна, и не вздумайте пробовать его без сопроцессора!

Сделайте наложение пиксель-на-пиксель из исходного изображение на целевой (используя свойство Canvas.Pixels). Для каждого пикселя осуществите преобразование полярных координат, добавьте компенсирующий угол к полярной координате, затем спозиционируйте это обратно на координаты прямоугольника, и разместите пиксель с новыми координатами на целевом изображении. Также вы можете добавлять какой-либо псевдослучайный пиксель через определенное их количество, если хотите задать какую-то точность вашей операции.

Для преобразования X- и Y-координат объявлены следующие переменные: X,Y = старые координаты пикселя X1,Y1 = новые координаты пикселя T = угол вращения (в радианах) R, A - промежуточные величины, представляющие собой полярные координаты R = Sqrt(Sqr(X) + Sqr(Y)); A = Arctan(Y/X); X1 = R * Cos(A+T); Y1 = R * Sin(A+T); Я отдаю себе отчет, что это не оптимальное решение, поэтому, если вы найдете еще какое-либо решение, дайте мне знать. В действительности мой метод работает, но делает это очень медленно.

Создайте наложение пиксель-на-пиксель исходного изображение на целевое (используя свойство Canvas.Pixels).

...это хорошее начало, но я думаю другой способ будет немного лучшим. Создайте наложение пиксель-на-пиксель целевого изображения на исходное так, чтобы нам было нужно вычислять откуда брать нужные пиксели, а не думать над тем, куда их нужно поместить.

Для начала вот мой вариант формулы вращения: x, y = координаты в целевом изображении t = угол u, v = координаты в исходном изображении x = u * cos(t) - v * sin(t) y = v * cos(t) + u * sin(t) Теперь, если я захочу решить эти уравнения и вычислить u и v (привести их к правой части уравнения), то формулы будут выглядеть следующим образом (без гарантии, по этой причине я и включил исходные уравнения!): x * cos(t) + y u = -------------------- sqr(cos(t)) + sin(t) v = y * cos(t) - x -------------------- sqr(cos(t)) + sin(t) Так, подразумевая, что вы уже знаете угол вращения, можно вычислить константы cos(t) и 1/sqr(cos(t))+sin(t) непосредственно перед самим циклом; это может выглядеть примерно так (приблизительный код):

    ct := cos(t);
ccst := 1/sqr(cos(t))+sin(t);
for x := 0 to width do
for
y := 0 to height do dest.pixels[x,y] := source.pixels[Round((x * ct + y) * ccst), Round((y * ct - x) * ccst)];

Если вы хотите ускорить этот процесс, и при этом волнуетесь за накопление ошибки округления, то вам следует обратить внимание на используемую нами технологию: мы перемещаем за один раз один пиксель, дистанция между пикселями равна u, v содержит константу, определяющую колонку с перемещаемым пикселем. Я использую расчитанные выше переменные как рычаг с коротким плечом (с вычисленной длиной и точкой приложения). Просто поместите в (x,y) = (1,0) и (x,y) = (0,1) и уравнение, приведенное выше:

    duCol := ct * ccst;
dvCol := -ccst;

duRow := ccst;
dvRow := ct * ccst;

uStart := 0;
vStart := 0;

for x := 0 to width do
begin
u := uStart; v := vStart; for y := 0 to height do begin dest.pixels[x,y] := source.pixels[Round(u), Round(v)]; u := u + rowdu; v := v + rowdv; end; uStart := uStart + duCol; vStart := vStart + dvCol; end;

Приведенный выше код можно использовать "как есть", и я не даю никаких гарантий отностительно его использования!

Если вы в душе испытатель, и хотите попробовать вращение вокруг произвольной точки, попробуйте поиграться со значенияим u и v: Xp, Yp (X-sub-p, Y-sub-p) точка оси вращения, другие константы определены выше x = Xp + (u - Xp) * cos(t) - (y - Yp) * sin(t) y = Yp + (y - Yp) * cos(t) - (x - Xp) * sin(t) Оригинальные уравнения: x = u * cos(t) - v * sin(t) y = v * cos(t) + u * sin(t) верны, но когда я решаю их для u и v, я получаю это: x * cos(t) + y * sin(t) u = ----------------------- sqr(cos(t)) + sqr(sin(t)) y * cos(t) - x * sin(t) v = ------------------------ sqr(cos(t)) + sqr(sin(t)) [001803]



Вывод текста на родительском элементе управления


Свойство Canvas в TCustomControl существует, но оно защищено. Поскольку свойство canvas инкапсулирует windows HDC (Canvas.Handle), вы можете создавать объект TCanvas и назначать через свойство Handle контекст устройства элементу управления, на котором вы хотите рисовать.

Для примера:

    procedure AControl.DrawLabel(ACaption: TCaption);
var
ACanvas: TCanvas; DC: HDC; begin
ACanvas:= TCanvas.Create; try WindowHandle:= parent.Handle; DC := GetDeviceContext(WindowHandle); ACanvas.Handle:= DC; with ACanvas do begin end; ACanvas.Handle:= 0; ReleaseDC(WindowHandle, DC); finally ACanvas.free; end; end;

[001621]



Заголовок TGA-файла


OK, берем книжку 'Graphics File Formats, 2nd Edition' (форматы графических файлов) авторов David C. Kay & John R. Levine, и читаем формат заголовка файла для хранения изображения формата Targa.

Описание заголовка файла для изображения формата Targa
Смещение Длина (в байтах) Описание
01Длина ID-поля (ID Field Length)
11 Тип цветовой карты (Color-map Type)
21 Тип изображения (Image Type)
      Информация о специфике цветовой карты (Color-map-specific Info)
32 Первое включение цветовой карты (First Color-map Entry)
52 Длина цветовой карты (Color-map Length)
71 Размер цветовой карты (Color-map Entry Size)
      Информация о специфике изображения (Image-specific Info)
82 Горизонтальная координата начала изображения (Image X Origin)
102 Вертикальная координата начала изображения (Image Y Origin)
122 Ширина изображения (Image Width)
142 Высота изображения (Image Height)
161 Бит на пиксел (Bits-Per-Pixel)
171 Биты дескриптора изображения (Image-Descriptor Bits)

Для изображений с разрешением True-color значение типа цветовой карты должно равняться нулю, в остальных случаях единице. В случае, когда цветовая карта присутствует, ее размер должен равняться значению 15, 16, 24 или 32. Для значений 15 и 16 каждая цветовая карта при загрузке использует 2 байта в формате:

Верхний байт Нижний байт A RRRRR GG GGG BBBBB

где бит 'A' устанавливается в 0 для 15-битных цветовых величин. 24-битный размер карты хранится как три байта в следующем порядке: (B)lue (синий), (G)reen (зеленый), и (R)ed (красный). 32-битный размер цветовой карты использует четыре байта, ее порядок такой: (B)lue (синий), (G)reen (зеленый), (R)ed (красный) и значение атрибута - (A)ttribute.

Наконец, код, хранящий тип изображения (Image Type) должен содержать одно из следующих значений:

Код Описание ---- ----------- 0 Изображение отсутствует 1 Цветовая карта, без компрессии 2 True-color, без компрессии 3 Черно-белое, без компрессии 9 Цветовая карта, RLE-компрессия 10 True-color, RLE-компрессия 11 Черно-белое, RLE-компрессия Горизонтальная и вертикальная координата начала изображения (Image X & Y Origins) и размеры изображения (Image Width & Height) разъяснений не требуют. Бит на пиксел (Bits-Per-Pixel) обозначает количество битов, содержащихся в точке изображения и может быть равен значению 8, 16, 24, и 32.

Биыт дескриптора изображения (The Image Descriptor bytes) содержит несколько полей битов, которые содержат следующую информацию:

Биты Описание ---- ----------- 0-3 Биты атрибутов (описаны ниже) 4 Ориентация Слева-на-Право 0=Л/П 1=П/Л 5 Ориентация Вехр/Низ 0=Н/В 1=В/Н 6-7 Чередование линий 00H=Нет, 40H=2 линии, 80H=4 линии Биты атрибутов используются для определения атрибутов цветов в цветовой карте или true-color пикселах. 0 - alpha-данные (alpha-канал) отсутствуют, 1 - игнорирование или неопределено, 2 - не определено, но должно быть сохранено, 3 - наличие alpha-данных и 4 - информация о пикселе уже была умножена на alpha-величину.

Файлы версии Targa 2.0 также имеют файловый колонтитул, который может содержать дополнительное изображение или комментарии. Эти файлы всегда заканчиваются строкой-терминатором 'TRUEVISION-TARGA.'. Так, если ваше Targa-изображение заканчивается значением 'TRUEVISION-TARGA.' + 00H, то вы можете извлечь восемь байтов до строки, чтобы найти начало расширенной области и месторасположение каталога сборки данного файла. Обычно файловый колонтитул версии 2.0 имеет следующий формат:

Формат файлового колонтитула Targa версии 2.0
Байт Длина Описание
0432-битное смещение расширенной области
44 32-битное смещение каталога сборки
817 TRUEVISION-TARGA.
251 Двоичный ноль ($0)

Я не собираюсь давать полные описания каталога сборки и области расширения. Вместо этого я приведу описание "почтовой марки", которая может содержаться в формате Targa V2.0. Данная "марка"-иконка должна иметь размеры 64 X 64 пикселей, представляет собой уменьшенный образ изображения, может включаться в файл по желанию компоновщика и не является обязательной.

Область расширения
Смещение Длина Описание
02Размер области расширения (должна быть 495)
241 Имя автора
4381 Авторские комментарии
12481 Авторские комментарии
20581 Авторские комментарии
28681 Авторские комментарии
3672 Месяц создания
3692 День создания
3712 Год создания
...... ...
4824 Смещение в файле таблицы цветовой коррекции
4864 Смещение в файле изображения "почтовой марки"
4904 Смещение в файле таблицы чередования линий
4941 Байты атрибутов

Данная "почтовая марка", при наличии, может быть использована вами непосредственно. Она хранится в виде несжатого изоюражения в том же цветовом формате (цветовой карте или True-color), как и исходное изображение. [000108]



Загрузка 256-цветного TBitmap


Windows не очень полезен, когда мы имеем дело с 256-цветными изображениями. Что делаю я (поскольку думаю, что это самый простой метод): я создаю в памяти изображение таким образом, чтобы TBitmap.LoadFromStream мог "принять" его. Данным методом я загружаю "сырой" ресурс изображения и размещаю его, используя инфорационный заголовок файла изображения. Вот потомок TBitmap, инкапсулирующий вышесказанное:

    type TMyBitmap = class(TBitmap) public procedure Load256ColorBitmap(Instance: THandle; BitmapName: PChar); end;
procedure TMyBitmap.Load256ColorBitmap(Instance: THandle; BitmapName: PChar); var HDib: THandle; Size: LongInt; Info: PBitmapInfo; FileHeader: TBitmapFileHeader; S: TMemoryStream; begin HDib := LoadResource(Instance, FindResource(Instance, BitmapName, RT_BITMAP)); if HDib <> 0 then begin Info := LockResource(HDib); Size := GetSelectorLimit(Seg(Info^)) + SizeOf(TBitmapFileHeader); with FileHeader do begin bfType := $4D42; bfSize := Size; bfReserved1 := 0; bfReserved2 := 0; bfOffBits := SizeOf(TBitmapFileHeader) + SizeOf(TBitmapInfoHeader); case Info^.bmiHeader.biBitCount of 1: bfOffBits := bfOffBits + 2 * 4; 4: bfOffBits := bfOffBits + 16 * 4; 8: bfOffBits := bfOffBits + 256 * 4; end; end; S := TMemoryStream.Create; try S.SetSize(Size); S.Write(FileHeader, SizeOf(TBitmapFileHeader)); S.Write(Info^, Size - SizeOf(TBitmapFileHeader)); S.Position := 0; LoadFromStream(S); finally S.Free; FreeResource(HDib); end; end else raise EResNotFound.Create(Format('Не могу найти ресурс изображения %s', [BitmapName])); end;

Вот как можно это использовать:

    Image1.Picture.Bitmap := TMyBitmap.Create; TMyBitmap(Image1.Picture.Bitmap).Load256ColorBitmap(hInstance, 'BITMAP_1');

-Steve Schafer [000709]



Загрузка иконок для TBitBtn и преобразование их к `no white` (без белого).


Если вы загружаете иконку в TImage, то этот компонент "понимает" как сделать ее прозрачной на основании полученной от нее информации. Реально иконки содержат два растровых изображения, одна из которых содержит информацию с нормальными цветами, в том числе с белым, который в дальнейшем должен стать прозрачной областью, второе же изображение представляет собой маску, состоящую из двух цветов, один из которых также белый, он необходим для создания прозрачной области, и черным цветом для цветных областей. Когда с двумя изображениями осуществляется операция "xor" вместе с фоном, белый цвет в белых областях для создания прозрачного фона (сквозь который проглядывает фон), цвет (включая белый) в черных областях для отображения иконки; (вы можете получить также области с "инвертированным фоном", они возникают в месте, где расположен черный цвет на первом изображении, но в этом же месте в изображении-маске находится белый цвет).

Когда вы преобразовываете иконку в растровое изображение, информация о прозрачности теряется, поскольку растровые изображения не имеют возможности хранения этого самого изображения-маски, которое используется для создания прозрачной области. Я догадываюсь что иконки, которые вы используете, являются "отдельностоящими" объектами с прозрачным фоном, обознащающим, что само изображение окружено белым фоном. Так, когда вы загружаете иконку в свойство Glyph компонента TBitbutton или TSpeedbutton, цвет пиксела левого нижнего угла (который в нашем случае будет белым) теперь будет интерпретироваться как цвет, задающий прозрачную область, и создающий именно тот эффект, который вы описали.

Решением будет преобразование иконки в растровое изображение, и сохранение ее как .BMP-файла, затем редактирование ее в ImageEdit, Resource Workshop, или даже PaintBrush, для задание цвета левого нижнего пиксела как цвета, задающего прозрачность. Правда, у меня есть код, который может извлечь из иконки цвет и изображение, задающее маску, но это тема уже следующей статьи. В ней мы рассмотрим способ обратного восстановления иконки по изображению и его маске. [001937]



Захват изображений


Вот пример кода, позволящего с помощью TBitmap захватить часть изображения и сохранить его в файле. Я включил функцию копирования палитры, необходимой при работе в режиме 256 цветов.

    function  CopyPalette( Palette : HPalette ) : HPalette ; var nEntries   : integer ; LogPalSize : integer ; LogPalette : PLogPalette ; begin Result := 0 ; if Palette = 0 then exit ; GetObject( Palette, sizeof( nEntries ), @nEntries ) ; if nEntries < 1 then exit ; LogPalSize := sizeof( TLogPalette ) + sizeof( TPaletteEntry ) * ( nEntries - 1 ) ; GetMem( LogPalette, LogPalSize ) ; with LogPalette^ do try palVersion := $300 ; palNumEntries := nEntries ; GetPaletteEntries( Palette, 0, nEntries, palPalEntry[ 0 ] ) ; Result := CreatePalette( LogPalette^ ) ; finally FreeMem( LogPalette, LogPalSize ) ; end ; end ;
procedure TForm1.Button1Click(Sender: TObject); var Bitmap : TBitmap ; begin Bitmap := TBitmap.Create ; try Bitmap.Width := 50 ; Bitmap.Height := 40 ; Bitmap.Palette := CopyPalette( Image1.Picture.Bitmap.Palette ) ; Bitmap.Canvas.CopyRect( Rect( 0, 0, 50, 40 ), Image1.Picture.Bitmap.Canvas, Bounds( 20, 10, 50, 40 ) ) ; Bitmap.SaveToFile( 'c:\windows\temp\junk.bmp' ) ; finally Bitmap.Free ; end ; end;

- Mike Scott. [000912]



Доступ к компонентам GroupBox


Одно из свойств всех элементов управления - указатель на другие элементы, которые он содержит. Это свойство - свойство Controls, которое индексируется наподобие массива. Количество элементов управления содержится в свойстве ControlCount. Если вы хотите получить доступ к свойству или методу, которого нет у TControl, вам неоходимо осуществить приведение типа элемента списка.

    procedure DoSomethingWithAGroupBox;
var
i : integer; begin
with
AGroupBox do for i := 0 to ControlCount - 1 do if controls[i] is TEdit then TEdit(controls[i]).text := 'Как насчет этого?'; end;
end;

Приведенный выше пример будет работать, если элементом управления является TEdit или его наследник, например, TDBEdit или TMaskEdit. Все объекты могут быть приведены к типу одного из объектов, являющегося наследником базового типа (или им самим). Но не спешите приводить все к родительскому классу, родитель в данном случае здесь не подходит, поскольку он означает объект, который содержит сам себя. [001447]



Рисование на GroupBox


Я хочу рисовать на холсте (Canvas) моего компонента GroupBox. Но когда я пробую рисовать на Component.Parent.Canvas, рисование происходит на форме, а не на моем компоненте GroupBox. Что я делаю неправильно?

Canvas - защищенное свойство TGroupBox и, поэтому, недоступное. Вы можете сделать его доступным следующим образом:

    type TMyGroupBox = class(TGroupBox)
public property Canvas; end;
procedure SomeProcedure;
begin
... with TMyGroupBox(GroupBox1).Canvas do CopyRect(ClipRect, Image1.Canvas, ClipRect); ... end;

- Ralph Friedman [001126]



Как можно узнать о готовности носителя без выскакивающего сообщения об ошибке?


Своим опытом делится Олег Кулабухов:

Можно использовать SetErrorMode() для предотвращения появления подобных сообщений.

    function IsDriveReady(DriveLetter : char) : bool;
var
OldErrorMode : Word;
OldDirectory : string;
begin
OldErrorMode := SetErrorMode(SEM_NOOPENFILEERRORBOX);
GetDir(0, OldDirectory);
{$I-}
ChDir(DriveLetter + ':\');
{$I+}
if IoResult <> 0 then
Result := False
else
Result := True;

ChDir(OldDirectory);
SetErrorMode(OldErrorMode);
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
if not
IsDriveReady('A') then
ShowMessage('Drive Not Ready') else
ShowMessage('Drive is Ready');
end;

[001867]



Как определить количество свободного места на диске размером более 2Gb?


Своим опытом делится Олег Кулабухов:

Для этого вам понадобится использовать GetDiskFreeSpaceEx() с последующим переводом целочисленных значений к типу Double.

    function GetDiskFreeSpaceEx(lpDirectoryName: PAnsiChar;
var lpFreeBytesAvailableToCaller : Integer;
var lpTotalNumberOfBytes: Integer;
var lpTotalNumberOfFreeBytes: Integer) : bool;
stdcall;
external kernel32
name 'GetDiskFreeSpaceExA';

procedure GetDiskSizeAvail(TheDrive : PChar;
var TotalBytes : double;
var TotalFree : double);
var
AvailToCall : integer;
TheSize : integer;
FreeAvail : integer;
begin
GetDiskFreeSpaceEx(TheDrive,
AvailToCall,
TheSize,
FreeAvail);
{$IFOPT Q+}
{$DEFINE TURNOVERFLOWON}
{$Q-}
{$ENDIF}
if TheSize >= 0 then
TotalBytes := TheSize else
if TheSize = -1 then begin
TotalBytes := $7FFFFFFF;
TotalBytes := TotalBytes * 2;
TotalBytes := TotalBytes + 1;
end else
begin

TotalBytes := $7FFFFFFF;
TotalBytes := TotalBytes + abs($7FFFFFFF - TheSize);
end;

if AvailToCall >= 0 then
TotalFree := AvailToCall else
if
AvailToCall = -1 then begin
TotalFree := $7FFFFFFF;
TotalFree := TotalFree * 2;
TotalFree := TotalFree + 1;
end else
begin

TotalFree := $7FFFFFFF;
TotalFree := TotalFree + abs($7FFFFFFF - AvailToCall);
end;
end;

procedure TForm1.Button1Click(Sender: TObject);
var
TotalBytes : double;
TotalFree : double;
begin
GetDiskSizeAvail('C:\',
TotalBytes,
TotalFree);
ShowMessage(FloatToStr(TotalBytes));
ShowMessage(FloatToStr(TotalFree));
end;

[001882]



Как получить серийный номер тома жесткого диска?


Своим опытом делится Олег Кулабухов:

Вот, посмотрите пример, в нем достается и еще кое-какая полезная информация.

    procedure TForm1.Button1Click(Sender: TObject);
var
VolumeName,
FileSystemName : array [0..MAX_PATH-1] of Char;
VolumeSerialNo : DWord;
MaxComponentLength,
FileSystemFlags : Integer;
begin
GetVolumeInformation('C:\',VolumeName,MAX_PATH,@VolumeSerialNo,
MaxComponentLength,FileSystemFlags,
FileSystemName,MAX_PATH);
Memo1.Lines.Add('VName = '+VolumeName);
Memo1.Lines.Add('SerialNo = $'+IntToHex(VolumeSerialNo,8));
Memo1.Lines.Add('CompLen = '+IntToStr(MaxComponentLength));
Memo1.Lines.Add('Flags = $'+IntToHex(FileSystemFlags,4));
Memo1.Lines.Add('FSName = '+FileSystemName);
end;

NeNashev дополняет:

Тут делал защиту CD по этому самому номеру и столкнулся со следующим: под NT системами серийник CD возвращается с обратным порядком байт, нежели под 9х. То есть, если под 9х Вы считали $11223344, то под NT считаете $44332211...

А серийник дискеты - не меняется... Про тома винчестера не скажу, не прверял.

А вообще серийный номер устанавливается при форматировании, и складывается их текущей даты/времени и еще чего-то... [001860]



Как распознать тип носителя?


Своим опытом делится Олег Кулабухов:

Используем функцию GetDriveType.

    procedure TForm1.Button1Click(Sender: TObject);
begin
case
GetDriveType('C:\') of
0 : ShowMessage('The drive type cannot be determined');
1 : ShowMessage('The root directory does not exist');
DRIVE_REMOVABLE:ShowMessage('The disk can be removed');
DRIVE_FIXED : ShowMessage('The disk cannot be removed');
DRIVE_REMOTE : ShowMessage('The drive is remote (network) drive');
DRIVE_CDROM : ShowMessage('The drive is a CD-ROM drive');
DRIVE_RAMDISK : ShowMessage('The drive is a RAM disk');
end;
end;

[001862]



Получение информации о диске


...я не нашел информации относительно функции 69h, но я вот что я нашел по поводу использования 4409h:

    type
MIDPtr = ^MIDRec; MIDRec = Record InfoLevel: word; SerialNum: LongInt; VolLabel: Packed Array [0..10] of Char; FileSysType: Packed Array [0..7] of Char; end;
function GetDriveSerialNum(MID: MIDPtr; drive: Word): Boolean; assembler;
asm
push  DS    { Просто для безопасности, я не думаю что это действительно нужно } mov   ax,440Dh { Функция получения ID устройства } mov   bx,drive    { номер устройства (0-по умолчанию, 1-A ...) } mov   cx,0866h  { код категории и минора } lds   dx,MID      { Загружаем pointeraddr. } call  DOS3Call   { Предположим, что это быстрее, чем INT 21H } jc    @@err mov   al,1           { No carry, поэтому возвращаем TRUE } jmp   @@ok @@err: mov   al,0          { Carry установлен, поэтому возвращаем FALSE } @@ok: pop   DS            { Восстанавливаем DS, так как не было предположений, что мы изменим это } end;

procedure TForm1.NrBtnClick(Sender: TObject);
var
Info: MIDRec; begin
Info.InfoLevel:=0; { Уровень информации } If GetDriveSerialNum(@Info,0) then  { Что-то с этим делаем... } ListBox.Items.Add(IntToStr(Info.SerialNum)+' '+Info.VolLabel); end;

[001956]



Серийный номер диска


Вот модуль Delphi, который читает эту информацию. Чтобы записать ее, необходимо перез вызовом прерывания изменить значение AX на $6901 и заполнить буфер вашими значениями. Требуется DOS 4.00+.

    unit Sernumu;

interface

uses

SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls;
type
TMediaID = Record InfoLevel : Word; SerialNumber : LongInt; VolumeLabel : Array[0..10] of Char; SysName : Array[0..7] of Char; End; TForm1 = class(TForm) Button1: TButton; Label1: TLabel; Label2: TLabel; Label3: TLabel; procedure Button1Click(Sender: TObject); private { Private declarations } MediaID : TMediaID; public { Public declarations } end;
var
Form1: TForm1;
implementation

{$R *.DFM}

type
DPMIRegisters = record DI : LongInt; SI : LongInt; BP : LongInt; Reserved : LongInt; BX : LongInt; DX : LongInt; CX : LongInt; AX : LongInt; Flags : Word; ES : Word; DS : Word; FS : Word; GS : Word; IP : Word; CS : Word; SP : Word; SS : Word; end;

function RealIntr(IntNo : Byte; var Regs : DPMIRegisters) : Word; Assembler; asm xor     bx,bx mov     bl,IntNo xor     cx,cx        {StackWords = 0} les     di,Regs mov     ax,0300h int     31h jc      @@ExitPoint xor     ax,ax @@ExitPoint: end;

function GetDiskInfo(Drive : Word; var MediaID : TMediaID) : Boolean;
type
tLong = Record LoWord, HiWord : Word; End; var
Regs : DPMIRegisters; dwAddress : LongInt; Address : tLong absolute dwAddress; begin
Result := False; FillChar(MediaID, SizeOf(MediaID), 0); dwAddress := GlobalDosAlloc(SizeOf(MediaID)); { два параграфа памяти DOS } if dwAddress = 0 then            { в случае ошибки адрес будет нулевым } exit;
With Regs do begin bx := Drive; cx := $66; ds := Address.HiWord; ax := $6900; dx := 0; es := 0; flags := 0; end; If RealIntr($21, Regs) <> 0 Then Exit; Move(ptr(Address.LoWord, 0)^, MediaID, SizeOf(MediaID)); GlobalDosFree(Address.LoWord)     { освобождаем блок DOS памяти } Result := True; end;

procedure TForm1.Button1Click(Sender: TObject);
begin
GetDiskInfo(1, MediaID); With MediaID do Begin Label1.Caption := IntToHex(SerialNumber, 8); Label2.Caption := VolumeLabel; Label3.Caption := SysName; End; end;

end.

[001955]



Серийный номер тома


Как с помощью Delphi 2.0 мне получить серийный номер винчестера?

Попробуй это:

    procedure TForm1.Button1Click(Sender: TObject);
var
SerialNum : dword; a, b : dword; Buffer  : array [0..255] of char; begin
if
GetVolumeInformation('c:\', Buffer, SizeOf(Buffer), @SerialNum, a, b, nil, 0) then Label1.Caption := IntToStr(SerialNum); end;

Исправленную версию прислал: Алексей Коган FIDOnet 2:5064/7.69 Russia, Stavropol Andy Dmitriev дополняет:

Должен заметить, что GetVolumeInformation возвращает серийный номер ТОМА, а не винчестера, то есть, если заменить C:\ на D:\, то номерок-то и поменяется... Определение серийного номера винчестера несколько сложнее, я встречал этот пример в одной из конференций, да и то оно не работало под NT/2000, кажется. В любом случае оглавление надо поправить, чтобы не дезинформировать читателей.

Поправляю... [000125]