3Delphi - база знаний

         

Изменение свойств печати во время ее выполнения


Изменение свойств печати во время ее выполнения




Как разрешить изменения свойств принтера (например, лоток с бумагой, ориентация и др.) между страницами печати одного документа в шести шагах.

(В совете также приведен пример изменения поддона с бумагой...)


*** ШАГИ ***
Создайте копию модуля Printers.pas и переименуйте его в NewPrint.pas.

***НЕ делайте изменения в самом модуле Printers.pas, если вы сделаете это, то получите во время компиляции приложения ошибку "Unable to find printers.pas" (не могу найти printer.pas). (Я уже получае ее, поэтому и упоминаю об этом здесь...)***


Переместите модуль NewPrint.pas в директорию Lib.

(Используйте "C:\Program Files\Borland\Delphi Х\Lib" )



Измените ИМЯ МОДУЛЯ на NewPrint.pas

с:
unit Printers 

на:
    unit NewPrint 


Добавьте декларацию следующего PUBLIC метода класса TPrinter в секции Interface модуля NewPrint.pas:

    procedure NewPageDC(DM: PDevMode); 


Добавьте следующую процедуру в секцию реализации NewPrint.pas:

procedure TPrinter.NewPageDC(DM: PDevMode);
begin
  CheckPrinting(True);
  EndPage(DC);
{Проверяем наличие новых установок для принтера}
  if Assigned(DM) then
    ResetDC(DC, DM^);
  StartPage(DC);
  Inc(FPageNumber);
  Canvas.Refresh;
end;


Вместо добавления "Printers" в секцию USES вашего приложения (список используемых модулей), добавьте "NewPrint".

Теперь вдобавок к старым методам (таким как BeginDoc, EndDoc, NewPage и др.), у вас появилась возможность изменения свойств принтера "на лету", т.е. между страницами при печати одного и того же документа. (Пример приведен ниже.)
Вместо вызова:

    Printer.NewPage; 

напишите:

    Printer.NewPageDC(DevMode); 

Вот небольшой пример:

procedure TForm1.Button1Click(Sender: TObject);
var
  ADevice, ADriver, APort: array[0..255] of char;
  ADeviceMode: THandle;
  DevMode: PDevMode;
begin
  with Printer do
    begin
      GetPrinter(ADevice, ADriver, APort, ADeviceMode);
      SetPrinter(ADevice, ADriver, APort, 0);
      GetPrinter(ADevice, ADriver, APort, ADeviceMode);
      DevMode := GlobalLock(ADeviceMode);
      if not Assigned(DevMode) then
        ShowMessage('Не могу установить принтер.')
      else
        begin
          with DevMode^ do
            begin
{Применяем здесь любые настройки, необходимые для изменения установок печати}
              dmDefaultSource := DMBIN_UPPER;
{этот код приведен в "Windows.pas"}
            end;
          GlobalUnlock(ADeviceMode);
          SetPrinter(ADevice, ADriver, APort, ADeviceMode);
        end;
    end;
  Printer.BeginDoc;
  Printer.Canvas.TextOut(50, 50, 'Эта страница печатается из ВЕРХНЕГО ЛОТКА.');
  with DevMode^ do
    begin
{Применяем здесь любые настройки, необходимые для изменения установок печати}
      dmDefaultSource := DMBIN_LOWER;
{этот код приведен в "Windows.pas"}
    end;
  Printer.NewPageDC(DevMode);
  Printer.Canvas.TextOut(50, 50, 'Эта страница печатается из НИЖНЕГО ЛОТКА.');
  Printer.EndDoc;
end;


Примечание от автора:

Я использовал это во многих своих программах, поэтому я уверен в работоспособности кода.

Данный кода был создан в Delphi Client/Server 2.01 под WinNT 4.0, но впоследствии был
проверен на других версиях Delphi, а также под Windows95.
Правда я еще не поробовал его под Delphi 4... Если вы имеете любые комментарии или улучшения,
не постесняйтесь отправить их мне...


Взято из

Советов по Delphi от


Сборник Kuliba






Изменить громкость


Изменить громкость




Эта программа увеличивает громкость выбранного канала на 1000:



usesMMSystem;

procedure TForm1.Button1Click(Sender: TObject);
var
  vol: longint;
  LVol, RVol: integer;
begin
  AuxGetVolume(ListBox1.ItemIndex, @Vol);
  LVol := Vol shr 16;
  if LVol < MaxWord - 1000 then
    LVol := LVol + 1000
  else
    LVol := MaxWord;
  RVol := (Vol shl 16) shr 16;
  if RVol < MaxWord - 1000 then
    RVol := RVol + 1000
  else
    RVol := MaxWord;
  AuxSetVolume(ListBox1.ItemIndex, LVol shl 16 + RVol);
end;

procedure TForm1.FormCreate(Sender: TObject);
var
  i: integer;
  cap: TAuxCaps;
begin
  for i := 0 to auxGetNumDevs - 1 do
  begin
    auxGetDevCaps(i, Addr(cap), SizeOf(cap));
    ListBox1.Items.Add(cap.szPname)
  end;
end;

procedure SetVolume(X: Word);
var
  iErr: Integer;
  i: integer;
  a: TAuxCaps;
begin
  for i := 0 to auxGetNumDevs do
  begin
    auxGetDevCaps(i, Addr(a), SizeOf(a));
    if a.wTechnology = AUXCAPS_CDAUDIO then
      break;
  end;

  // Устанавливаем одинаковую громкость для левого и правого каналов.
  // VOLUME := LEFT*$10000 + RIGHT*1

  iErr := auxSetVolume(i, (X * $10001));
  if (iErr‹›0) then
    ShowMessage('No audio devices are available!');
end;

function GetVolume: Word;
var
  iErr: Integer;
  i: integer;
  a: TAuxCaps;
  vol: word;
begin
  for i := 0 to auxGetNumDevs do
  begin
    auxGetDevCaps(i, Addr(a), SizeOf(a));
    if a.wTechnology = AUXCAPS_CDAUDIO then
      break;
  end;
  iErr := auxGetVolume(i, addr(vol));
  GetVolume := vol;
  if (iErr‹›0) then
    ShowMessage('No audio devices are available!');
end;

unit Volumes;
 
interface 
 
uses 
  Windows, Messages, Classes, ExtCtrls, ComCtrls, MMSystem; 

const 
  CDVolume       = 0; 
  WaveVolume     = 1; 
  MidiVolume     = 2; 
 
type 
  TVolumeControl = class(TComponent) 
  private
    FDevices     : array[0..2] of Integer; 
    FTrackBars   : array[0..2] of TTrackBar; 
    FTimer       : TTimer; 
    function       GetInterval: Integer; 
    procedure      SetInterval(AInterval: Integer);
    function       GetVolume(AIndex: Integer): Byte; 
    procedure      SetVolume(AIndex: Integer; aVolume: Byte); 
    procedure      InitVolume; 
    procedure      SetTrackBar(AIndex: Integer; ATrackBar: TTrackBar); 
    { Private declarations } 
    procedure      Update(Sender: TObject); 
    procedure      Changed(Sender: TObject); 
  protected 
    { Protected declarations } 
    procedure      Notification(AComponent: TComponent; AOperation: 
TOperation); override; 
  public 
    { Public declarations } 
    constructor    Create(AOwner: TComponent); override; 
    destructor     Destroy; override; 
  published 
    { Published declarations } 
    property       Interval: Integer read GetInterval write SetInterval default 
500; 
    property       CDVolume: Byte index 0 read GetVolume write SetVolume stored 
False; 
    property       CDTrackBar: TTrackBar index 0 read FTrackBars[0] write 
SetTrackBar; 
    property       WaveVolume: Byte index 1 read GetVolume write SetVolume 
stored False; 
    property       WaveTrackBar: TTrackBar index 1 read FTrackBars[1] write 
SetTrackBar; 
    property       MidiVolume: Byte index 2 read GetVolume write SetVolume
stored False; 
    property       MidiTrackBar: TTrackBar index 2 read FTrackBars[2] write 
SetTrackBar; 
  end; 
 
procedure Register; 
 
implementation 
 
procedure Register; 
begin 
  RegisterComponents('Any', [TVolumeControl]); 
end; 
 
type 
    TVolumeRec = record 
    case Integer of 
    0: (LongVolume: Longint); 
    1: (LeftVolume, 
        RightVolume : Word); 
    end; 
 
    function       TVolumeControl.GetInterval: Integer; 
    begin 
      Result := FTimer.Interval; 
    end; 
 
    procedure      TVolumeControl.SetInterval(AInterval: Integer);
    begin 
      FTimer.Interval := AInterval; 
    end; 
 
    function       TVolumeControl.GetVolume(AIndex: Integer): Byte; 
    var Vol: TVolumeRec; 
    begin 
      Vol.LongVolume := 0; 
      if FDevices[AIndex] < >  -1 then 
      case AIndex of 
      0: auxGetVolume(FDevices[AIndex], @Vol.LongVolume); 
      1: waveOutGetVolume(FDevices[AIndex], @Vol.LongVolume); 
      2: midiOutGetVolume(FDevices[AIndex], @Vol.LongVolume); 
      end; 
      Result := (Vol.LeftVolume + Vol.RightVolume) shr 9; 
    end; 
 
    procedure      TVolumeControl.SetVolume(aIndex: Integer; aVolume: Byte); 
    var Vol: TVolumeRec; 
    begin 
      if FDevices[AIndex] < >  -1 then 
      begin 
        Vol.LeftVolume := aVolume shl 8; 
        Vol.RightVolume := Vol.LeftVolume; 
        case AIndex of 
        0: auxSetVolume(FDevices[AIndex], Vol.LongVolume); 
        1: waveOutSetVolume(FDevices[AIndex], Vol.LongVolume); 
        2: midiOutSetVolume(FDevices[AIndex], Vol.LongVolume);
        end; 
      end; 
    end; 
 
    procedure      TVolumeControl.SetTrackBar(AIndex: Integer; ATrackBar: 
TTrackBar); 
    begin 
      if ATrackBar < >  FTrackBars[AIndex] then 
      begin 
        FTrackBars[AIndex] := ATrackBar; 
        Update(Self); 
      end; 
    end; 
 
 AOperation: TOperation); 
    var I: Integer; 
    begin 
      inherited Notification(AComponent, AOperation); 
      if (AOperation = opRemove) then 
      for I := 0 to 2 do if (AComponent = FTrackBars[I]) 
      then FTrackBars[I] := Nil; 
    end; 
 
    procedure      TVolumeControl.Update(Sender: TObject); 
    var I: Integer; 
    begin 
      for I := 0 to 2 do 
      if Assigned(FTrackBars[I]) then
      with FTrackBars[I] do 
      begin 
        Min := 0; 
        Max := 255; 
        if Orientation = trVertical 
        then Position := 255 - GetVolume(I) 
        else Position := GetVolume(I); 
        OnChange := Self.Changed; 
      end; 
    end; 
 
    constructor    TVolumeControl.Create(AOwner: TComponent); 
    begin 
      inherited Create(AOwner); 
      FTimer := TTimer.Create(Self); 
      FTimer.OnTimer := Update; 
      FTimer.Interval := 500; 
      InitVolume; 
    end; 
 
    destructor     TVolumeControl.Destroy; 
    var I: Integer; 
    begin 
      FTimer.Free; 
      for I := 0 to 2 do 
      if Assigned(FTrackBars[I]) then 
      FTrackBars[I].OnChange := Nil; 
      inherited Destroy;
    end; 
 
    procedure      TVolumeControl.Changed(Sender: TObject); 
    var I: Integer; 
    begin 
      for I := 0 to 2 do 
      if Sender = FTrackBars[I] then 
      with FTrackBars[I] do 
      begin 
        if Orientation = trVertical 
        then SetVolume(I, 255 - Position) 
        else SetVolume(I, Position); 
      end; 
    end; 
 
    procedure      TVolumeControl.InitVolume; 
    var AuxCaps     : TAuxCaps; 
        WaveOutCaps : TWaveOutCaps; 
        MidiOutCaps : TMidiOutCaps; 
        I,J         : Integer; 
    begin 
      FDevices[0] := -1; 
      for I := 0 to auxGetNumDevs - 1 do 
      begin 
        auxGetDevCaps(I, @AuxCaps, SizeOf(AuxCaps)); 
        if (AuxCaps.dwSupport and AUXCAPS_VOLUME) < >  0 then 
        begin 
          FTimer.Enabled := True;
          FDevices[0] := I; 
          break; 
        end; 
      end; 
      FDevices[1] := -1; 
      for I := 0 to waveOutGetNumDevs - 1 do 
      begin 
        waveOutGetDevCaps(I, @WaveOutCaps, SizeOf(WaveOutCaps)); 
        if (WaveOutCaps.dwSupport and WAVECAPS_VOLUME) < >  0 then 
        begin 
          FTimer.Enabled := True; 
          FDevices[1] := I; 
          break; 
        end; 
      end; 
      FDevices[2] := -1; 
      for I := 0 to midiOutGetNumDevs - 1 do 
      begin 
        MidiOutGetDevCaps(I, @MidiOutCaps, SizeOf(MidiOutCaps)); 
        if (MidiOutCaps.dwSupport and MIDICAPS_VOLUME) < >  0 then 
        begin 
          FTimer.Enabled := True; 
          FDevices[2] := I; 
          break; 
        end; 
      end;
    end;

end.

Выставь на форму 2 тракбара и двигай их. Если у тебя звучит музыка, ты должен услышать изменения громкости правого и левого каналов.



procedure TForm1.TrackBar1Change(Sender: TObject);
var
 s: dword;
 a,b: word;
 h: hWnd;
begin
 a:=trackbar1.position;
 b:=trackbar2.position;
 s:=(a shl 16) or b;
 waveOutSetVolume(h,s);
end;

 


свойство Max в каждом TrackBar'e должно равняться 65535 (то есть MaxWord)




Взято с






Изменить размер поля или его тип


Изменить размер поля или его тип




Автор: Reinhard Kalinke

Единственный способ изменить размер поля или его тип - использовать DBIDoRestructure. Вот простой пример, который может вам помочь в этом:


functionBDEStringFieldResize(ATable: TTable; AFieldName: string; ANewSize:
  integer): boolean;
type
  TRestructStatus = (rsFieldNotFound, rsNothingToDo, rsDoIt);
var
  hDB: hDBIdb;
  pTableDesc: pCRTblDesc;
  pFldOp: pCROpType; {фактически это массив array of pCROpType}
  pFieldDesc: pFldDesc; {фактически это массив array of pFldDesc}
  CurPrp: CurProps;
  CSubType: integer;
  CCbrOption: CBRType;
  eRestrStatus: TRestructStatus;
  pErrMess: DBIMsg;
  i: integer;
begin
  Result := False;
  eRestrStatus := rsFieldNotFound;
  AFieldName := UpperCase(AFieldName);
  pTableDesc := nil;
  pFieldDesc := nil;
  pFldOp := nil;

  with ATable do
  try

    {убедимся что имеем исключительный доступ и сохраним dbhandle:}
    if Active and (not Exclusive) then
      Close;
    if (not Exclusive) then
      Exclusive := True;
    if (not Active) then
      Open;
    hDB := DBHandle;

    {готовим данные для DBIDoRestructure:}
    BDECheck(DBIGetCursorProps(Handle, CurPrp));
    GetMem(pFieldDesc, CurPrp.iFields * sizeOf(FldDesc));
    BDECheck(DBIGetFieldDescs(Handle, pFieldDesc));
    GetMem(pFldOp, CurPrp.iFields * sizeOf(CROpType));
    FillChar(pFldOp^, CurPrp.iFields * sizeOf(CROpType), 0);

    {ищем в цикле (через fielddesc) наше поле:}
    for i := 1 to CurPrp.iFields do
    begin
      {для ввода мы имеем серийные номера вместо
      Pdox ID, возвращаемых DbiGetFieldDescs:}
      pFieldDesc^.iFldNum := i;
      if (Uppercase(StrPas(pFieldDesc^.szName)) = AFieldName)
        and (pFieldDesc^.iFldType = fldZSTRING) then
      begin
        eRestrStatus := rsNothingToDo;
        if (pFieldDesc^.iUnits1 <> ANewSize) then
        begin
          pFieldDesc^.iUnits1 := ANewSize;
          pFldOp^ := crModify;
          eRestrStatus := rsDoIt;
        end;
      end;
      inc(pFieldDesc);
      inc(pFldOp);
    end; {for}

    {"регулируем" массив указателей:}
    dec(pFieldDesc, CurPrp.iFields);
    dec(pFldOp, CurPrp.iFields);

    {в случае отсутствия операций возбуждаем исключение:}
    case eRestrStatus of
      rsNothingToDo: raise Exception.Create('Ничего не сделано');
      rsFieldNotFound: raise Exception.Create('Поле не найдено');
    end;

    GetMem(pTableDesc, sizeOf(CRTblDesc));
    FillChar(pTableDesc^, SizeOf(CRTblDesc), 0);
    StrPCopy(pTableDesc^.szTblName, TableName);
    {StrPCopy(pTableDesc^.szTblType,szPARADOX); {}
    pTableDesc^.szTblType := CurPrp.szTableType;
    pTableDesc^.iFldCount := CurPrp.iFields;
    pTableDesc^.pecrFldOp := pFldOp;
    pTableDesc^.pfldDesc := pFieldDesc;

    Close;

    BDECheck(DbiDoRestructure(hDB, 1, pTableDesc, nil, nil, nil, False));

  finally
    if pTableDesc <> nil then
      FreeMem(pTableDesc, sizeOf(CRTblDesc));
    if pFldOp <> nil then
      FreeMem(pFldOp, CurPrp.iFields * sizeOf(CROpType));
    if pFieldDesc <> nil then
      FreeMem(pFieldDesc, CurPrp.iFields * sizeOf(FldDesc));
    Open;
  end; {пробуем с table1}
  Result := True;
end;


Взято из



Примечание Vit: На счёт "Единственный способ" - этот товарищ несколько погорячился. Все базы данных поддерживают SQL запрос вида

ALTER TABLE...

Конкретный формат надо выяснить в справочнике по используемой базе данных, так как он немного различается для разных серверов, но указанный запрос весьма гибок, и применим не только с BDE, но и с другими системами доступа и с любыми базами данных.




Изменяем заголовок окна


Изменяем заголовок окна



Автор: Christian Cristofori

В примере показывается, как изменять заголовок окна (видимый в списке задач при переключении между приложениями) при минимизации окна в иконку.

Сперва необходимо определить сообщение поумолчанию:

Const 
  DefMsgNorm = 'MyApp version 1.0'; 
  DefMsgIcon = 'MyApp. (Use F12 to turn of)'; 

И добавить две глобальных переменных:

Var 
  ActMsgNorm : String; 
  ActMsgIcon : String; 

Затем при открытии основной формы инициализируем переменные из констант.

Procedure TFormMain.FormCreate( Sender : TObject ); 
Begin 
  ActMsgNorm := DefMsgNorm; 
  ActMsgIcon := DefMsgIcon; 
  Application.Title := ActMsgNorm; 
End; 

Затем достаточно в обработчик OnResize добавить следующий код:

Procedure TFormMain.FormResize( Sender : TObject ); 
Begin 
  If ( FormMain.WindowState = wsMinimized ) Then 
    Application.Title := ActMsgIcon 
  Else 
    Application.Title := ActMsgNorm; 
End; 


Взято с Исходников.ru



Изображения и InterBase Blob-поля


Изображения и InterBase Blob-поля




dBASE и Paradox таблицы имеют в своем арсенале BLOB-поля, позволяющие хранить бинарные данные, в том числе bitmap-формат, отображаемый с помощью компонента TDBImage. В Database Desktop данный тип полей указан как Binary и Graphic (для dBASE и Paradox таблиц, соответственно). Тем не менее, процесс сохранения изображений в InterBase BLOB-полях и их использование в компонентах TDBImage не такой уж простой.

Таблицы InterBase не имеют простого типа BLOB-поля. Есть три варианта, или подтипа: тип 0, тип 1 и подтип, определенный пользователем. Типы 0 и 1 - "встроенные" типы. Тип 0 - BLOB-поля (тип по умолчанию) для хранения общих бинарных данных. Тип 1 - BLOB-поля для хранения текстовых BLOB-данных. Ни один из предопределенных типов не допускает автоматического извлечения данных изображения из BLOB-поля для его последующего отображения в компоненте TDBImage. BLOB-поля типа 0 могут использоваться для хранения данных bitmap-формата, но данные должны извлекаться и передаваться в объект типа TBitmap программным путем. Вот пример ручного извлечения данных изображения, хранящихся в BLOB-поле типа 0 (Table1BLOBField), и его показ в компоненте TImage (не предназначенным для работы с БД) :



procedureTForm1.ExtractBtnClick(Sender: TObject);
begin
  Image1.Picture.Bitmap.Assign(Table1BLOBField);
end;




Естественно, поскольку это должно делаться вручную, данный процесс менее желателен в приложении, нежели автоматическое отображение данных изображения в комбинации BDE и компонента TDBImage. Здесь происходит определение подтипа определенного пользователем BLOB-поля. При работе с данными подтип BLOB-поля учитывается, т.к. сохраненные первыми данные устанавливают тип данных для этого поля для всей таблицы целиком. Таким образом, если данные bitmap-формата оказывается первым загружаемым типом, то данный формат будет единственно возможным для данного поля. До сих пор по умолчанию тип бинарного BLOB-поля (предопределенный тип 0) позволял BDE читать и отображать данные в компоненте TDBImage без особых проблем.

Утилиты Database Desktop допускают создание бинарных BLOB-полей только типа 0 и не имеют возможности самим определять подтипы BLOB-полей. Из-за такого ограничения таблицы, подразумевающие хранение и вывод изображений, должны создаваться с помощью SQL-запросов. Обычно это делается посредством утилиты WISQL, но вполне достаточно выполнение SQL-запроса с помощью компонента TQuery. Ниже приведен SQL-запрос, создающий таблицу с определенным пользователем подтипом BLOB-поля:



CREATE TABLE WITHBMP
(
  FILENAME CHAR(12),
  BITMAP   BLOB SUB_TYPE -1
)




После создания таблицы с совместимыми BLOB-полями, для хранения данных изображения в BLOB-поле и его вывода в компоненте TDBImage используются те же самые методы, что и при работе с таблицами dBASE и Paradox.

Имеется множество способов загрузки изображений в BLOB-поле. Три самых простых метода включают в себя:

копирование данных из буфера обмена Windows в компонент TDBImage, связанный с BLOB-полем
использование метода LoadFromFile компонента TBLOBField
использование метода Assign для копирования объекта типа TBitmap в значение свойства Picture компонента TBDBImage.
Первый способ, когда происходит копирование изображения из буфера обмена, вероятно, наиболее удобен в случае, когда необходимо добавить изображение в таблицу при использовании приложения конечным пользователем. В этом случае компонент TDBImage используется в роли интерфейса между BLOB-полем таблицы и изображением, хранящимся в буфере обмена. Метод PasteFromClipboard компонента TDBImage как раз и занимается тем, что копирует изображение из буфера обмена в TDBImage. При сохранении записи изображение записывается в BLOB-поле таблицы.
Поскольку буфер обмена Windows может содержать данные различных форматов, то желательно перед вызовом метода CopyFromClipboard осуществлять проверку формата хранящихся в нем данных. Для этого необходимо создать объект TClipboard и использовать его метод HasFormat, позволяющий определить формат хранящихся в буфере данных. Имейте в виду, что для создания объекта TClipboard вам необходимо добавить модуль Clipbrd в секцию uses того модуля, в котором будет создаваться экземпляр объекта.

Вот исходный код примера, копирующий содержание буфера обмена в компонент TDBImage, если содержащиеся в буфере данные имеют формат изображения:



procedure TForm1.Button1Click(Sender: TObject);
var
  C: TClipboard;
begin
  C := TClipboard.Create;
  try
    if Clipboard.HasFormat(CF_BITMAP) then
      DBImage1.PasteFromClipboard
    else
      ShowMessage('Буфер обмена не содержит изображения!');
  finally
    C.Free;
  end;
end;





Второй способ заполнения BLOB-поля заключается в загрузке изображения непосредственно из файла в BLOB-поле. Данный способ одинаково хорош как при создании приложения (формирование данных), так и при его использовании.

Этот способ использует метод LoadFromFile компонента TBLOBField, который применяется в Delphi для работы с dBASE-таблицами и двоичными Windows полями или таблицами Paradox и графическими Windows полями; в обоих случаях с помощью данного метода возможно загрузить изображение и сохранить его в таблице.

Методу LoadFromFile компонента TBLOBField необходим единственный параметр типа String: имя загружаемого файла с изображением. Значение данного параметра может быть получено при выборе файла пользователем с помощью компонента TOpenDialog и его свойства FileName.

Вот пример, демонстрирующий работу метода LoadFromFile компонента TBLOBField с именем Table1Bitmap (поле с именем Bitmap связано с таблицей TTable, имеющей имя Table1):



procedure TForm1.Button2Clicck(Sender: TObject);
begin
  Table1Bitmap.LoadFromFile(
    'c:\delphi\images\splash\16color\construc.bmp');
end;




Третий способ для копирования содержимого объекта типа TBitmap в свойство Picture компонента TDBImage использует метод Assign. Объект типа TBitmap может быть как свойством Bitmap свойства-объекта Picture компонента TImage, так и отдельного объекта TBitmap. Как и в методе, копирующем данные из буфера обмена в компонент TDBImage, данные изображения компонента TDBImage сохраняются в BLOB-поле после успешного сохранения записи.

Ниже приведен пример, использующий метод Assign. В нашем случае используется отдельный объект TBitmap. Для помещения изображения в компонент TBitmap был вызван его метод LoadFromFile.



procedure TForm1.Button3Click(Sender: TObject);
var
  B: TBitmap;
begin
  B := TBitmap.Create;
  try
    B.LoadFromFile('c:\delphi\images\splashh\16color\athena.bmp');
    DBImage1.Picture.Assign(B);
  finally
    B.Free;
  end;
end;



Взято с





Изучаем ассемблер в Delphi


Изучаем ассемблер в Delphi



Автор: Ian Hodger

Основное предназначение этой статьи, заполнить пробелы в оригинальной документации по Borland Delphi Developer, при этом весь программный код, а так же теория, полность совместимы со всеми версиями Delphi.

Основное направление статьи, это познакомиться с использованием ассемблера в Object Pascal. Однако, не будем пропускать и те аспекты программирования, которые будут требовать пояснения для конкретных примеров, приведённых в этой статье.

Использование Ассемблера в Борландовком Delphi
Перед тем, как начать, хотелось бы определиться с уровнем знаний, необходимых для нормального усвоения данного материала. Необходимо быть знакомым со встроенными средствами отладки в Delphi. Так же необходимо иметь представление о таких терминах как тип реализации (instantiation), null pointer и распределение памяти. Если в чём-то из вышеупомянутого Вы сомневаетесь, то постарайтесь быть очень внимательны и осторожны при воплощении данного материала на практике. Кроме того, будет обсуждаться только 32-битный код, так что понадобится компилятор не ниже Delphi 2.0.

Зачем использовать Ассемблер?
На мой взгляд, Object Pascal, это инструмент, позволяющий генерировать быстрый и эффективный код, однако использование ассемблера в некоторых случаях позволяет решать некоторые задачи более эффективно. За всю работу с Delphi, я пришёл к выводу, что использование низкоуровневого кода необходимо в двух случая.

(1) Обработка большого количества данных. Nb. В данный случай не входит ситуация, когда используется язык запроса данных.

(2) В высокоскоростных подпрограммах работы с дисплеем. Nb. Имеется ввиду использование простых процедур на чистом паскале, но никак не внешних библиотек и DirectX.

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

Что такое Ассемблер?
Надеюсь, что Все читатели этой статьи имеют как минимум поверхностное представление о работе процессора. Грубо говоря, это калькулятор с большим объёмом памяти. Память, это не более чем упорядоченная последовательнось двоичных цифр. Каждая такая цифра является байтом. Каждый байт может содержать в себе значение от 0 до 255, а так же имеет свой уникальный адрес, при помощи которого процессор находит нужные значения в памяти. Процессор так же имеет набор регистров (это можно расценить как глобальные переменные). Например eax,ebx,ecx и edx, это универсальные 32-битные регистры. Это значит, что самое большое число, которое мы можем записать в регистр eax, это 2 в степени 32 минус 1, или 4294967295.

Как мы уже выяснили, процессор манипулирует значениями регистров. Машинный код операции прибавления 10 к значению регистра eax будет выглядеть следующим образом
05/0a/00/00/00
Однако, такая запись абсолютно не читабельна и, как следствие, не пригодна при отладке программы. Так вот Ассемблер, это простое представление машинных команд в более удобном виде. Теперь давайте посмотрим, как будет выглядеть прибавление 10 к eax в ассемблерном представлении:
add eax,10 {a := a + 10}
А вот так выглядит вычитаение значения ebx из eax
sub eax,ebx {a := a - b }
Чтобы сохранить значние, можно просто поместить его в другой регистр
mov eax,ecx {a := c }
или даже лучше, сохранить значение по определённому адресу в памяти
mov [1536],eax {сохраняет значение eax по адресу 1536}
и конечно же взять его от туда
mov eax,[1536]

Однако, тут есть важный момент, про который забывать не желательно. Так как регистр 32-битный(4 байта), то его значение будет записано сразу в четыре ячейки памяти 1536, 1537, 1538 и 1539.

А теперь давайте посмотрим, как компилятор преобразует действия с переменными в машинный код. Допустим у нас есть строка
Count := 0;
Для компилятора это означает, что надо просто запомнить значение. Следовательно, компилятор генерирует код, который сохраняет значение в памяти по определённому адресу и следит, чтобы не произошло никаких накладок, и обзывает этот адрес как 'Count'. Вот как выглядит такой код

mov eax,0 
mov Count,eax 

Компилятор не может использовать строку типа

mov Count,0
из-за того, что как минимум один параметр инструкции должен являться регистром.
(см. примечание в конце *)

Если посмотреть на строку
Count := Count + 1;
то её ассемблерное представление будет выглядеть как
mov eax,Count
add eax,1
mov Count,eax
(см. примечание в конце **)
Для переменных, тип которых отличается от целого, всё усложняется. Однако, рассмотрим эту тему немного позже, а сейчас предлагаю закрепить теорию практическими примерами.

Итак, рассмотрим первый пример. Сразу извинюсь за тривиальность, но с чего-то надо начинать.

function Sum(X,Y:integer):integer; 
begin 
 Result := X+Y; 
end; 

А вот так будет выглядеть оперция сложения двух целых чисел на ассемблере:

function Sum(X,Y:integer):integer; 
begin 
 asm
  mov eax,X
  add eax,Y
  mov Result,eax
 end;
end;

Этот код прекрасно работает, однако он не даёт нам преимущества в скорости, а так же потерялось восприятие кода. Но не стоит огорчаться, так как те немногие знания, которые Вы почерпнули из этого материала, можно использовать с большей пользой. Допустим, нам необходимо преобразовать явные значения Red,Green, и Blue в цвета типа TColor, подходящие для использования в Delphi. Тип TColor описан как 24-битный True Colour хранящийся в формате целого числа, то есть четыре байта, старший из которых равен нулю, а далее по порядку красный, зелёный, синий.

function GetColour(Red,Green,Blue:integer):TColor; 
begin 
 asm
{ecx будет содержать значение TColor}
  mov ecx,0 
{начинаем с красной компоненты}
  mov eax,Red 
{необходимо убедиться, что красный находится в диапазоне 0<=Red<=255}
  and eax,255 
{сдвигаем значение красного в правильное положение}
  shl eax,16 
{выравниваем значение TColor}
  xor ecx,eax 
{проделываем тоже самое с зелёным}
  mov eax,Green 
  and eax,255
  shl eax,8
  xor ecx,eax
{и тоже самое с синим}
  mov eax,Blue 
  and eax,255
  xor ecx,eax
  mov Result, ecx
 end;
end; 

Заметьте, что я использовал несколько бинарных операций. Эти операции также определены непосредственно в Object Pascal.

Взято с Исходников.ru

Примечание * от Jin X
Чушь! Во-первых, параметры обязаны быть регистрами только в очень редких случаях (например, при чтении/записи из/в порт: out 20h,al), а во-вторых, компилятор Delphi7 генерирует именно mov Count,12345678h при использовании Count := $12345678. Но! Когда мы делаем Count := 0, то генерируется пара xor eax,eax + mov Count,eax , причём лишь в целях экономии памяти (такая запись короче в машинном представлении).

Примечание ** от Jin X
это тоже не есть правда, компилятор делает гораздо проще: inc Count






Извлечение изображения из BLOB-поля


Извлечение изображения из BLOB-поля





Извлечение изображения из BLOB-поля таблицы dBASE или Paradox -- без первой записи изображения в файл -- простейший процесс использования метода Assign для сохранения содержимого BLOB-поля в объекте, имеющим тип TBitmap. Отдельный объект TBitmap или свойство Bitmap объекта Picture, в свою очередь являющегося свойством компонента TIMage, могут служить примером совместимой цели для данной операции.

Вот пример кода, демонстрирующего использование метода Assign для копирования изображения из BLOB-поля в компонент TImage.

procedureTForm1.Button1Click(Sender: TObject);
begin
  Image1.Picture.Bitmap.Assign(Table1Bitmap);
end;

В данном примере, объект Table1Bitmap типа TBLOBField - BLOB-поле таблицы dBASE. Данный TBLOBField-объекты был создан с помощью редактора полей (Fields Editor). Если редактор полей для создания TFields для полей таблицы не используется, получить доступ к полям можно с помощью метода FieldByName или свойства Fields, оба они являются членами компонентов TTable или TQuery. В случае ссылки на BLOB-поле таблицы с помощью одного из приведенных членов, перед использованием метода Assign указатель на поле должен быть прежде приведен к типу объекта TBLOBField. Для примера:

procedure TForm1.Button1Click(Sender: TObject);
begin
  Image1.Picture.Bitmap.Assign(TBLOBField(Table1.Fields[1]));
end;

Изображение, хранящееся в BLOB-поле, может быть скопировано непосредственно в отдельный TBitmap объект. Ниже приведен пример, демонстрирующий создание объекта TBitmap и сохранения в нем изображения из BLOB-поля.

procedure TForm1.Button2Click(Sender: TObject);
var
  B: TBitmap;
begin
  B := TBitmap.Create;
  try
    B.Assign(Table1Bitmap);
    Image1.Picture.Bitmap.Assign(B);
  finally
    B.Free;
  end;
end;

 

Взято из







Ярлыки, файловые ассоциации, расширения


Ярлыки, файловые ассоциации, расширения



Cодержание раздела:











См. также статьи в других разделах:





Является ли шрифт шрифтом с фиксированной шириной?


Является ли шрифт шрифтом с фиксированной шириной?




procedureTConsole.FontChanged(Sender: TObject);
var
  DC: HDC;
  Save: THandle;
  Metrics: TTextMetric;
  Temp: string;
begin
  if Font.Handle <> FOldFont.Handle then
  begin
    DC := GetDC(0);
    Save := SelectObject(DC, Font.Handle);
    GetTextMetrics(DC, Metrics);
    SelectObject(DC, Save);
    ReleaseDC(0, DC);
    if not (((Metrics.tmPitchAndFamily and ff_Modern) <> 0) and
      ((Metrics.tmPitchAndFamily and $01) = 0)) then
    begin
      Temp := 'TConsole: ' + Font.Name +
        ' не является шрифтом с фиксированной шириной';
      Font.Name := FOldFont.Name; { Возвращаем предыдущие атрибуты шрифта }
      raise EInvalidFont.Create(Temp);
    end;
    SetMetrics(Metrics);
  end;
  FOldFont.Assign(Font);
  if csDesigning in ComponentState then
    InternalClrScr;
end;


Взято из





Язык программирования Дельфи


Язык программирования Дельфи



Нет, я не ошибся! Начиная с версии Delphi 7 фирма Борланд официально называет язык программирования Delphi и Kylix языком "Дельфи". В этом разделе собраны вопросы по работе с конструкциями языка Дельфи, не затрагивая конкретной реализации большинства классов VCL.

Vit



Cодержание раздела:




·
·  
·  
·  
·  
·  
·  

 


·
·  
·  
·  
·  
·  
·  
·  
·  
·  
·  
·  
·  
·  
·  
·  
·  
·  
·  
·  
·  
·  
·  
·  
·  
·  



·  
·  
·  
·  
·  
·  
·  
·  
·  
·  
·  
·  
·




·
·  
·  
·  
·  
·  
·  
·  
·  
·  
·

 



·
·  
·  
·  
·  
·  
·  
·  
·

 



·
·  
·  
·  
·  
·  
·  
·  
·

 



·
·  
·  
·  
·

 





·
·  
·  
·  
·  
·  
·  
·  
·  
·  
·  
·  
·  
·  
·  
·  
·  
·  
·

 



·
·  
·  
·  
·  
·  
·  
·  
·  



·  
·  
·  
·  




·
·

 



·






Эффект плавного перехода


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




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

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



procedureWaitAWhile(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, но должно быть лучшее решение. Хорошо, допустим приведенный способ лучший, но он кажется неправильным...)

Взято с





Экспорт ADO таблиц в разные форматы


Экспорт ADO таблиц в разные форматы






Exporting ADO tables into various formats 

In this article I want to present a component I built in order to 
supply exporting features to the ADOTable component. ADO supplies 
an extended SQL syntax that allows exporting of data into various  
formats. I took into consideration the following formats: 

1)Excel 
2)Html 
3)Paradox 
4)Dbase 
5)Text 

You can see all supported output formats in the registry: 
"HKEY_LOCAL_MACHINE\Software\Microsoft\Jet\4.0\ISAM formats" 

This is the complete source of my component } 

unit ExportADOTable; 

interface 

uses 
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, 
  Db, ADODB; 

type 
  TExportADOTable = class(TADOTable) 
  private 
    { Private declarations } 
    //TADOCommand component used to execute the SQL exporting commands 
    FADOCommand: TADOCommand; 
  protected 
    { Protected declarations } 
  public 
    { Public declarations } 
    constructor Create(AOwner: TComponent); override; 

    //Export procedures 
    //"FiledNames" is a comma separated list of the names of the fields you want to export 
    //"FileName" is the name of the output file (including the complete path) 
    //if the dataset is filtered (Filtered = true and Filter <> ''), then I append  
    //the filter string to the sql command in the "where" directive 
    //if the dataset is sorted (Sort <> '') then I append the sort string to the sql command in the  
    //"order by" directive 
    
    procedure ExportToExcel(FieldNames: string; FileName: string; 
      SheetName: string; IsamFormat: string); 
    procedure ExportToHtml(FieldNames: string; FileName: string); 
    procedure ExportToParadox(FieldNames: string; FileName: string; IsamFormat: string); 
    procedure ExportToDbase(FieldNames: string; FileName: string; IsamFormat: string); 
    procedure ExportToTxt(FieldNames: string; FileName: string); 
  published 
    { Published declarations } 
  end; 

procedure Register; 

implementation 

procedure Register; 
begin 
  RegisterComponents('Carlo Pasolini', [TExportADOTable]); 
end; 

constructor TExportADOTable.Create(AOwner: TComponent); 
begin 
  inherited; 

  FADOCommand := TADOCommand.Create(Self); 
end; 


procedure TExportADOTable.ExportToExcel(FieldNames: string; FileName: string; 
  SheetName: string; IsamFormat: string); 
begin 
  {IsamFormat values 
   Excel 3.0 
   Excel 4.0 
   Excel 5.0 
   Excel 8.0 
  } 

  if not Active then 
    Exit; 
  FADOCommand.Connection  := Connection;   
  FADOCommand.CommandText := 'Select ' + FieldNames + ' INTO ' + '[' + 
    SheetName + ']' + ' IN ' + '"' + FileName + '"' + '[' + IsamFormat + 
    ';]' + ' From ' + TableName; 
  if Filtered and (Filter <> '') then 
    FADOCommand.CommandText := FADOCommand.CommandText + ' where ' + Filter; 
  if (Sort <> '') then 
    FADOCommand.CommandText := FADOCommand.CommandText + ' order by ' + Sort; 
  FADOCommand.Execute; 
end; 

procedure TExportADOTable.ExportToHtml(FieldNames: string; FileName: string); 
var 
  IsamFormat: string; 
begin 
  if not Active then 
    Exit; 

  IsamFormat := 'HTML Export'; 

  FADOCommand.Connection  := Connection; 
  FADOCommand.CommandText := 'Select ' + FieldNames + ' INTO ' + '[' + 
    ExtractFileName(FileName) + ']' +  
    ' IN ' + '"' + ExtractFilePath(FileName) + '"' + '[' + IsamFormat + 
    ';]' + ' From ' + TableName; 
  if Filtered and (Filter <> '') then 
    FADOCommand.CommandText := FADOCommand.CommandText + ' where ' + Filter; 
  if (Sort <> '') then 
    FADOCommand.CommandText := FADOCommand.CommandText + ' order by ' + Sort; 
  FADOCommand.Execute; 
end; 


procedure TExportADOTable.ExportToParadox(FieldNames: string; 
  FileName: string; IsamFormat: string); 
begin 
  {IsamFormat values 
  Paradox 3.X 
  Paradox 4.X 
  Paradox 5.X 
  Paradox 7.X 
  } 
  if not Active then 
    Exit; 

  FADOCommand.Connection  := Connection; 
  FADOCommand.CommandText := 'Select ' + FieldNames + ' INTO ' + '[' + 
    ExtractFileName(FileName) + ']' +  
    ' IN ' + '"' + ExtractFilePath(FileName) + '"' + '[' + IsamFormat + 
    ';]' + ' From ' + TableName; 
  if Filtered and (Filter <> '') then 
    FADOCommand.CommandText := FADOCommand.CommandText + ' where ' + Filter; 
  if (Sort <> '') then 
    FADOCommand.CommandText := FADOCommand.CommandText + ' order by ' + Sort; 
  FADOCommand.Execute; 
end; 

procedure TExportADOTable.ExportToDbase(FieldNames: string; FileName: string; 
  IsamFormat: string); 
begin 
  {IsamFormat values 
  dBase III 
  dBase IV 
  dBase 5.0 
  } 
  if not Active then 
    Exit; 

  FADOCommand.Connection  := Connection; 
  FADOCommand.CommandText := 'Select ' + FieldNames + ' INTO ' + '[' + 
    ExtractFileName(FileName) + ']' +  
    ' IN ' + '"' + ExtractFilePath(FileName) + '"' + '[' + IsamFormat + 
    ';]' + ' From ' + TableName; 
  if Filtered and (Filter <> '') then 
    FADOCommand.CommandText := FADOCommand.CommandText + ' where ' + Filter; 
  if (Sort <> '') then 
    FADOCommand.CommandText := FADOCommand.CommandText + ' order by ' + Sort; 
  FADOCommand.Execute; 
end; 

procedure TExportADOTable.ExportToTxt(FieldNames: string; FileName: string); 
var 
  IsamFormat: string; 
begin 
  if not Active then 
    Exit; 

  IsamFormat := 'Text'; 

  FADOCommand.Connection  := Connection; 
  FADOCommand.CommandText := 'Select ' + FieldNames + ' INTO ' + '[' + 
    ExtractFileName(FileName) + ']' +  
    ' IN ' + '"' + ExtractFilePath(FileName) + '"' + '[' + IsamFormat + 
    ';]' + ' From ' + TableName; 
  if Filtered and (Filter <> '') then 
    FADOCommand.CommandText := FADOCommand.CommandText + ' where ' + Filter; 
  if (Sort <> '') then 
    FADOCommand.CommandText := FADOCommand.CommandText + ' order by ' + Sort; 
  FADOCommand.Execute; 
end; 

end. 


Note that you can use an already existing database as destination but not an already existing 
table in the database itself: if you specify an already exixting table you will receive 
an error message. You might insert a verification code inside every exporting procedure of my 
component, before the execution of the sql exporting command, in order to send a request of   
deleting the already present table or aborting the exporting process. 

carlo Pasolini, Riccione(italy), e-mail: ccpasolini@libero.it 


Взято с сайта




Экспорт анимированных 3D персонажей из 3D STUDIO MAX 3.0 для DELPHI и OpenGL


Экспорт анимированных 3D персонажей из 3D STUDIO MAX 3.0 для DELPHI и OpenGL




Введение

В свое время я здорово помучался, решая вопрос - каким же образом создатели игр ухитряются делать трехмерные персонажи двигающиеся в реальном времени. Я предположил, что части тела у персонажей отделены от основного тела, что позволяет независимо поворачивать и перемещать их. Знаете, в Direct3D даже есть понятие фрейма, фрейм - это основное тело, к нему прикрепляются другие тела. Когда фрейм движется, прикрепленные к нему объекты движутся вместе с ним, кроме того, прикрепленные объекты могут двигаться и самостоятельно не влияя на движение фрейма. Все это замечательно подходит для создания механических объектов и персонажей, но совершенно не годится для создания объектов живого мира. Для таких объектов характерна плавность линий и отсутствие изломов на местах стыков частей объекта. Создатели компьютерных игр замечательно решили эту проблему.

Как создается двумерная анимация? Рисуется несколько кадров движения, затем поледовательно выводятся на экран и таким образом создается иллюзия движения. То же самое происходит в современных трехмерных компьютерных играх. Создается несколько 3D моделей (сеток), характеризующих фазы движения персонажа в различные моменты времени, затем поледовательно выводятся на экран, создавая иллюзию движения. Возможно, это приводит к повышенному расходу оперативной памяти, поскольку все сетки желательно хранить в памяти, но зато значительно упрощается процесс программирования и, скорее всего, увеличивается скорость работы приложения.

Следующая проблема возникла при попытке экспорта объектов из 3D Studio Max в какой-либо открытый формат, например DXF. Нет ничего сложного в создании трехмерного персонажа с последующей его анимаций, если пользоваться 3D Studio и Character Studio, вся проблема состоит в том, как экспортировать объект чтобы потом файл с сетками объекта можно было использовать в своем приложении. Для этого требуется покадровый экспорт анимированного персонажа, то есть в итоге должен получится файл, содержащий несколько сеток объекта изображающих фазы движений объекта в различные моменты времени, или несколько файлов содержащих одну сетку соответствующую определенному кадру движения. Однако, несмотря на обилие поддерживаемых форматов файлов, 3D Studio Max не обладает возможностью покадрового экспорта трехмерных объектов. Так, напрмер, файл формата 3DS может хранить информацию о положении объекта, его повороте и масштабе, но не в состоянии сохранять деформации сетки в различных кадрах анимации, а именно это нам и нужно. Про файлы формата DXF и ASC даже говорить в данном случае смешно. Я объясню, почему нам нужно сохранять именно деформацию сетки. Дело в том, что наш объект должен состоять из единой, цельной сетки, а не из нескольких объектов, чтобы не было стыков на местах соединений конечностей с телом. Создать анимацию, так чтобы персонаж мог двигать своими конечностями, в этом случае, можно только деформируя сетку, а именно перемещая одни вершины сетки относительно других. Так, например, чтобы персонаж поднял руку нужно переместить вершины руки вверх относительно вершин тела. Теперь, я надеюсь, все понятно? Итак, оказалось, что 3DStudio не в состоянии сохранить подобную анимацию. Однако, не все так печально. Например, есть такой дополнительный модуль для 3DStudio, называется Bones Pro Max, а у него есть инструмент SnapShot, который позволяет делать снимки различных кадров движения объекта. В результате его работы у Вас на рабочем поле 3D Studio Max появляется целое стадо одинаковых трехмерных объектов в различных позах. Правду сказать, я его не нашел, да и выпущен он был уже давно еще под первую версию 3D Studio Max. Поэтому я решил идти другим путем и окунулся во внутренний язык 3D Studi Max - Max Script. Результатом моей деятельности стала простенькая утилита Meshes Export for Games and Animation (MEGA), которая позволяет делать все, о чем я сказал выше и некоторые другие полезные вещи.

Знакомство с утилитой MEGA V 1.0

Для ознакомления с этой утилитой Вам понадобится графический пакет 3D Studio Max 3.0 и, собственно, сама утилита. Она расположена в папке Utility и называется MEGA.ms. Это не исполняемый файл, а текстовый файл с набором команд для 3D Studio Max написанных на языке Max Script.

Запустите 3D Studio Max и создайте простой объект - сферу. Я полагаю, что даже те, кто никогда не видел этого графического редактора, без труда справятся с таким простым заданием.

Теперь, щелкайте на сфере правoй кнопкой мыши пока не появится контекстное меню. Как правило, с первого раза оно не появляется. В контекстном меню выберите строку Convert to Editable Mesh (Преобразовать в Редактируемую Сетку). Обратите внимание: объект, непременно, должен быть Редактируемой Сеткой, если в выходном файле мы хотим получить список вершин и граней, иначе мы получим имя объекта и его свойства, такие как, радиус, количество сегментов - для сферы, высоту, ширину и глубину - для параллелепипеда и т.д.

Перейдите на командную панель (она расположена справа) и выберите вкладку с изображением молотка. Это вкладка утилит. Нажмите кнопку MAXScript, внизу панели развернется свиток MAXScript'a. Нажмите кнопку Запуск Макроса, появится диалоговое окно открытия файла. Запустите файл MEGA.ms. Внизу командной панели в списке утилит должна появится надпись MEGA, однако это еще не означает, что утилита уже запущена. Чтобы ее запустить, необходимо раскрыть спиок утилит и выделить строку MEGA. Внизу панели должен раскрыться свиток MEGA.

Введите в поле From зачение 1, в поле To - 100, в поле Step - 100. Нажмите кнопку Save As..., в диалоговом окне введите имя файла, куда бдете сохранять и нажмите кнопку сохранить. Объект экспортирован в файл с расширением GMS.

Как работает утилита: При экспорте файла, берется значение из поля From и ползунок счетчика кадров расположенный внизу экрана премещается на позицию, соответствующую этому значению. Затем в выходной файл экспортируется объект в том виде, в каком он пребывает на данный момент на экране. После чего снова передвигается ползунок кадров на величину, введенную в поле Step. Снова записывается модель соответствующая этому кадру. И так до тех пор, пока ползунок не переместится на позицию соответствующую значению, введенному в поле To. Поскольку в данном примере мы не создавали анимацию, то нам нужен был только один кадр. Утилита экспортировала кадр №1, затем добавила к нему значение 100. Номер кадра стал равен 101. Поскольку это значение больше значения введенного в поле To, процесс экспорта на этом остановился. Если бы в поле From было введено значение 0, то было бы экспортировано 2 кадра с номерами 0 и 100 соответственно. Если пометить галочкой опцию Selected Only, то экспортироваться будут только выделенные объекты, это иногда бывает очень нужно, в противном случае будут экспортированы все объекты сцены. Теперь рекомендую рассмотреть формат файла GMS.

Формат файла GMS

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



// Указывает на начало нового объекта,
// следующая строка указывает тип объекта
New object
TriMesh() // Объект - сетка
// Указывает, что следующая строка содержит количество
// вершин и граней для данного объекта
numverts numfaces
Mesh vertices:
// Здесь располагается блок вершин объекта в виде координат X Y Z
end vertices

Mesh faces:
// Здесь располагается блок граней объекта в виде индексов 1 2 3,
// где каждый индекс - индекс в массиве вершин, указывает на вершину грани
end faces

Faset normals:
// Здесь располагается блок фасетных нормалей в виде координат X Y Z.
// Их количество равно количеству граней
end faset normals

Smooth normals:
// Здесь располагается блок сглаживающих нормалей в виде координат X Y Z.
// Их количество равно количеству вершин.
end smooth normals

end mesh // Конец описания объекта Tri Mesh
end of file // Конец файла




Примерно так выглядит файл, когда мы экспортируем сетчатый объект. Если объект не сетчатый, то файл будет выглядеть следующим образом:



// Указывает на начало нового объекта,
// следующая строка указывает тип объекта
New object
<Тип объекта>, например: Box

// Здесь идут параметры, зависящие от типа объекта
// (Поверхности Безье и NURBS - поверхности не поддерживаются)

end <Тип объекта> // Конец описания объекта
end of file // Конец файла




Загрузка файла формата GMS в Delphi

Пример загрузки файла GMS находится в папке Ch01. В проекте присутствует два модуля: frmMain.pas и Mesh.pas. Откомпилировав и запустив проект на выполнение вы должны увидеть вращающийся Тор (по-нашему: "Баранка"). Несмотря на то, что объект можно считать стандартным, он был в 3D Studio преобразован в сетку, поэтому в данном случае это именно сетчатый объект. Нажав пункт меню "загрузить", вы можете посмотреть любой объект из папки GMS или загрузить свою сферу, которую сделали сами, если правильно руководствовались моими инструкциями в разделе: Знакомство с утилитой MEGA V1.0. Теперь рассмотрим данный пример подробно. Почти весь код модуля frmMain.pas написан не мной. Он взят из книги "OpenGL графика в проектах Delphi" Михаила Краснова. Этот модуль выполняет инициализацию приложения и циклическую функцию отрисовки окна, поэтому подробно мы его рассматривать не будем. Если код покажется Вам непонятным, значит Вы недостаточно знакомы с OpenGL, в этом случае Вам надлежит обратится к первоисточнику (в смысле - к книге). Код модуля Mesh.pas выполняет загрузку данных из файла и отображение объектов в окне. Рассмотрим его подробнее:



//Объявление типов данных
type
  // Указатель на вершину
  PGLVertex = ^TGLVertex;
  TGLVertex = record
    // Вершина, как три значения с плавающей точкой
    x, y, z: GLFloat;
  end;
  // Указатель на вектор
  PGLVector = ^TGLVector;
  // Вектор, как массив из трех элементов с плавающей точкой
  TGLVector = array[0..2] of GLFloat;
  // Указатель на грань
  PGLFace = ^TGLFace;
  // Грань, как массив из трех целочисленных значений
  TGLFace = array[0..2] of GLInt;
  // Указатель на массив вершин
  PGLVertexArray = ^TGLVertexArray;
  // Массив вершин
  TGLVertexArray = array[Word] of TGLVertex;
  // Указатель на массив граней
  PGLFacesArray = ^TGLFacesArray;
  // Массив граней
  TGLFacesArray = array[word] of TGLFace;




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

Теперь рассмотрим описание объекта сетка:



TGLMesh = class
  // Массив вершин объекта - сетка
  Vertices : PGLVertexArray;
  // Массив граней
  Faces : PGLFacesArray;
  // Массив фасетных нормалей
  FasetNormals : PGLVertexArray;
  // Количество вершин
  VertexCount : Integer;
  // Количество граней
  FacesCount : Integer;
  // Коэффициент масштабирования
  fExtent : GLFloat;
  // Флаг масштабирования
  Extent : GLBoolean;
public
  // Загрузка
  procedure LoadFromFile(const FileName: string);
  // Расчет нормалей
  procedure CalcNormals;
  // Отрисовка
  procedure Draw;
  // Уничтожение с очисткой массивов
  destructor Destroy; override;
end;




Здесь пояснений практически не требуется. Можно лишь отметить, что Extent служит для того, чтобы объект загнать в размеры в пределах (-1, 1), я сделал это для того, чтобы объект любого размера не мог вылезти за пределы окна. Вообще говоря, в 3D Studio Max не сложно масштабировать объект так, чтобы координаты вершин попали в интервал (-1, 1), но на этапе создания модели думать об этом совсем не хочется.



// Загрузка файла
procedure TGLMesh.LoadFromFile;
var
  f : TextFile;
  S : string;
  i : Integer;
  Vertex : TGLVertex;
  Face : TGLFace;
  MaxVertex : GLFloat;
begin

  AssignFile(f,FileName);
  Reset(f);
  // Пропускаем строки, пока не попадется 'numverts numfaces'
  repeat
    ReadLn(f, S);
  until
    (S = 'numverts numfaces') or eof(f);

  // Читаем количество вершин и граней
  Readln(f,VertexCount,FacesCount);

  // Выделяем память для хранения сетки
  GetMem(Vertices,VertexCount*SizeOf(TGLVertex));
  GetMem(Faces,FacesCount*SizeOf(TGLFace));
  GetMem(FasetNormals,FacesCount*SizeOf(TGLVector));

  // Пропускаем строку "Mesh vertices"
  ReadLn(f, S);

  // Считываем вершины
  for i := 0 to VertexCount - 1 do
  begin
    Readln(f,Vertex.x,Vertex.y,Vertex.z);
    Vertices[i] := Vertex;
  end;

  // Пропускаем строку "end vertices"
  ReadLn(f, S);
  // Пропускаем строку "Mesh faces"
  ReadLn(f, S);

  // Считываем грани
  for i := 0 to FacesCount - 1 do
  begin
    Readln(f,Face[0],Face[1],Face[2]);
    Face[0] := Face[0] - 1;
    Face[1] := Face[1] - 1;
    Face[2] := Face[2] - 1;
    Faces[i] := Face;
  end;

  CloseFile(f);

  // Рассчитываем масштаб
  MaxVertex := 0;

  for i := 0 to VertexCount - 1 do
  begin
    MaxVertex := Max(MaxVertex,Vertices[i].x);
    MaxVertex := Max(MaxVertex,Vertices[i].y);
    MaxVertex := Max(MaxVertex,Vertices[i].z);
  end;

  fExtent := 1/MaxVertex;
  CalcNormals;
end;




Здесь могут быть непонятны следующие моменты: В блоке считывания граней я вычитаю единицу из каждого индекса вершины, считанного из файла. Делается это потому, что в программе индексы нумеруются, начиная с нуля, а в файле GMS - начиная с единицы. Процедура CalcNormals служит для расчета нормалей и взята из книги "OpenGL графика в проектах Delphi" Михаила Краснова. О том, что такое нормали и зачем они нужны я расскажу в разделах "Фасетные нормали" и "Сглаживающие нормали".



procedure TGLMesh.Draw;
var
  i : Integer;
  Face : TGLFace;
begin
  if Extent then
    glScalef(fExtent,fExtent,fExtent);

  for i := 0 to FacesCount - 1 do
  begin
    glBegin(GL_TRIANGLES);
    Face := Faces[i];
    glNormal3fv(@FasetNormals[i]);
    glVertex3fv(@Vertices[Face[0]]);
    glVertex3fv(@Vertices[Face[1]]);
    glVertex3fv(@Vertices[Face[2]]);
    glEnd;
  end;
end;




Здесь все понятно. Сначала, если установлен флаг масштабирования, устанавливается масштаб одинаковый по всем осям, затем в цикле рисуются треугольники. Перед началом рисования треугольника объявляется нормаль к нему. В качестве параметров передаются не конкретные значения, а указатели на них.



destructor TGLMesh.Destroy;
begin
  FreeMem(Vertices,VertexCount*SizeOf(TGLVertex));
  FreeMem(Faces,FacesCount*SizeOf(TGLFace));
  FreeMem(FasetNormals,FacesCount*SizeOf(TGLVector));
end;




Здесь тоже все понятно, просто освобождается память, занятая объектом. Вызовы процедур загрузки и отрисовки объекта находятся в модуле frmMain и не представляют ничего интересного.

Создание анимированного персонажа и вывод на экран

Специально для тех, кто не владеет навыками работы с 3D Studio Max и Character Studio, я создал модель бегающего человечка. Она находится в папке MAX, и файл называется BodyRun.max. Если у Вас вообще нет пакета 3D Studio Max, то файл GMS с сетками этого человечка находится в папке GMS и называется ManRun.gms.

Итак, запустите среду 3D Studio Max и создайте анимированного персонажа или загрузите его из файла BodyRun.max. Запустите утилиту MEGA, как это делалось в разделе Знакомство с утилитой MEGA V1.0. Установите значение поля From =0, значение поля To установите в кадр, на котором заканчивается анимация, в случае с файлом BodyRun.max это значение нужно установить в 11. Значение поля Step установите в еденицу. Выделите сетку персонажа.

Внимание:

убедитесь, что Вы выделили именно сетку персонажа и только ее. Пометьте флажок Selected Only. Для анимации сетки используется скелет. Он создается и подгоняется под размеры и форму тела, затем вершины сетки связываются с костями скелета. При анимации изменяются параметры положения частей скелета, а сетка лишь следует за ними. Поэтому, всегда, когда используется этот подход, в сцене помимо сетки присутствует скелет. Вот почему необходимо выделить только сетку и пометить флажок Selected Only.

После того, как Вы выполнили все операции укзанные выше, экспортируйте объект в файл GMS. В процессе экспорта Вы должны увидеть, как последовательно перемещается ползунок расположенный внизу экрана, отсчитывая кадры анимации, и как меняются кадры в проекционных окнах 3D Studio Max. Процесс экспорта завершится, когда ползунок достигнет конечного значения.

Готовый проект лежит в папке Ch02. Откомпилируйте его и запустите на выполнение. Нажатием кнопки "Анимировать" можно запускать или останавливать анимацию. Если Ваш компьютер оснащен 3D ускорителем, то лучше развернуть окно на весь экран - так медленнее. Теперь разберем исходный код программы. Он дополнился новым объектом TGLMultyMesh, который создан для загрузки и последовательной отрисовки нескольких сетчатых объектов.



TGLMultyMesh = class
    Meshes : TList;
    CurrentFrame : Integer;
    Action : Boolean;
    fExtent : GLFloat;
    Extent : Boolean;
  public
    procedure LoadFromFile(const FileName: string);
    procedure Draw;
    constructor Create;
    destructor Destroy; override;
  published
end;




Список Meshes хранит все сетки загруженные из файла. Переменная Action указывает выполняется анимация или нет, а CurrentFrame содержит номер текущего кадра анимации.



procedure TGLMultyMesh.LoadFromFile;
var
  f : TextFile;
  S : string;

  procedure ReadNextMesh;
  var
    i : Integer;
    Vertex : TGLVertex;
    Face : TGLFace;
    MaxVertex : GLFloat;
    NextMesh : TGLMesh;
  begin
    NextMesh := TGLMesh.Create;
    repeat
      ReadLn(f, S);
    until
      (S = 'numverts numfaces') or eof(f);
    // Читаем количество вершин и граней
    Readln(f,NextMesh.VertexCount,NextMesh.FacesCount);
    // Выделяем память для хранения сетки
    GetMem(NextMesh.Vertices,NextMesh.VertexCount*SizeOf(TGLVertex));
    GetMem(NextMesh.Faces,NextMesh.FacesCount*SizeOf(TGLFace));
    GetMem(NextMesh.FasetNormals,NextMesh.FacesCount*SizeOf(TGLVector));
    ReadLn(f,S); // Пропускаем строку Mesh vertices:
    // Считываем вершины
    for i := 0 to NextMesh.VertexCount - 1 do
    begin
      Readln(f,Vertex.x,Vertex.y,Vertex.z);
      NextMesh.Vertices[i] := Vertex;
    end;
    ReadLn(f,S); // Пропускаем строку end vertices
    ReadLn(f,S); // Пропускаем строку Mesh faces:
    // Считываем грани
    for i := 0 to NextMesh.FacesCount - 1 do
    begin
      Readln(f,Face[0],Face[1],Face[2]);
      Face[0] := Face[0] - 1;
      Face[1] := Face[1] - 1;
      Face[2] := Face[2] - 1;
      NextMesh.Faces[i] := Face;
    end;
    // Рассчитываем масштаб
    MaxVertex := 0;
    for i := 0 to NextMesh.VertexCount - 1 do
    begin
      MaxVertex := Max(MaxVertex,NextMesh.Vertices[i].x);
      MaxVertex := Max(MaxVertex,NextMesh.Vertices[i].y);
      MaxVertex := Max(MaxVertex,NextMesh.Vertices[i].z);
    end;
    NextMesh.fExtent := 1/MaxVertex;
    NextMesh.CalcNormals;
    Meshes.Add(NextMesh);
  end;

begin
  Meshes := TList.Create;
  AssignFile(f,FileName);
  Reset(f);
  while not Eof(f) do
  begin
    Readln(f,S);
    if S = 'New object' then
      ReadNextMesh;
  end;
  CloseFile(f);
end;




Код загрузки объекта TGLMultyMesh практически идентичен коду загрузки объекта TGLMesh. Небольшое отличие состоит в том, что объект TGLMultyMesh предполагает, что файл содержит несколько сеток. Поэтому при загрузке проиходит поиск строки "New Object", создается объект TGLMesh, который помещается в список Meshes и в него считывается информация из файла. Затем весь цикл повторяется до тех пор, пока не кончится файл. Процедуры создания, уничтожения и отрисовки объекта тоже почти не изменились:



procedure TGLMultyMesh.Draw;
begin
  if Extent then
  begin
    fExtent := TGLMesh(Meshes.Items[CurrentFrame]).fExtent;
    glScalef(fExtent,fExtent,fExtent);
  end;
  // Рисование текущего кадра
  TGLMesh(Meshes.Items[CurrentFrame]).Draw;
  // Если включена анимация увеличить значение текущего кадра
  if Action then
  begin
    inc(CurrentFrame);
    if CurrentFrame > (Meshes.Count - 1) then
      CurrentFrame := 0;
  end;
end;

constructor TGLMultyMesh.Create;
begin
  Action := False;
  CurrentFrame := 0;
end;

destructor TGLMultyMesh.Destroy;
var
  i : Integer;
begin
  for i := 0 to Meshes.Count - 1 do
    TGLMesh(Meshes.Items[i]).Destroy;
  Meshes.Free;
end;




Немного изменился и вызов функции загрузки в модуле frmMain.pas.



procedure TfrmGL.N1Click(Sender: TObject);
begin
  if OpenDialog.Execute then
  begin
    MyMesh.Destroy;
    Mymesh := TGLMultyMesh.Create;
    MyMesh.LoadFromFile( OpenDialog.FileName );
    MyMesh.Extent := true;
    // Проверяем сколько сеток загружено и возможна ли анимация
    if MyMesh.Meshes.Count <= 1 then
      N2.Enabled := False
    else
      N2.Enabled := True;
  end;
end;

// Включение анимации
procedure TfrmGL.N2Click(Sender: TObject);
begin
  MyMesh.Action := not MyMesh.Action;
  N2.Checked := not N2.Checked;
end;




Здесь все должно быть предельно ясно, не будем акцентировать на этом внимание, и так статья длиннее получается, чем я расчитывал.

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

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

Что такое нормали

Нормалью называется перпендикуляр к чему-либо. В нашем случае это перпендикуляр к грани. Хотелось бы, но, к сожалению, без нормалей никак не обойтись. Дело в том, что по нормалям расчитывается освещение объекта. Так, например, если нормаль грани направлена на источник света, то грань будет освещена максимально. Чем больше нормаль отвернется от источника света, тем менее грань будет освещена. В случае с OpenGL, если нормаль отвернется от экрана более чем на 90 градусов, мы вообще не увидим грань, она не будет отрисовываться. Если бы мы не использовали нормали, то наш объект был бы закрашен одним цветом, то есть мы бы увидели только силует объекта. Трехмерный эффект достигается окрашиванием граней объекта в разные по яркости цвета, или наложением теней, кому как больше нравится это называть. Кроме того, степень освещенности зависит также от длины вектора нормали, но, как правило, длина вектора нормали должна находится в пределах (0; 1).

Теперь я думаю, стало ясно, что такое нормали и зачем они нужны.

Загрузка фасетных нормалей из файла GMS

Что такое фасетная нормаль? Фасетная нормаль, это самая обычная нормаль к грани, а называется она так по производимому воздействию на изображаемый объект. После применения фасетных нормалей грани объекты хоть и освещены по-разному, но каждая грань освещена равномерно и соответственно закрашена одним цветом, что приводит к тому, что объект выглядит граненым. Отсюда и название. По-нашему "фасетная нормаль" это "граненая нормaль". В предыдущих примерах фасетные нормали рассчитывались по математическому алгоритму (процедура CalcNormals), но по всей видимости он иногда дает сбои. Не все то хорошо для программиста, что хорошо для математика. В результате и появляются черные треугольники там где их не должно быть.

К счастью, внутренний язык 3D Studio Max позволил мне найти фасетные нормали, которые он использовал для отображения объекта, а отображались объекты в 3D Studio Max правильно. Приложение, использующее нормали, взятые из 3D Studio Max, находится в папке Ch03. А какая при этом получается разница, Вы можете увидеть на картинках ниже:

Теперь наша баранка выглядит правильно. В процедуре загрузки сетки добавился блок считывания фасетных нормалей из файла GMS. Процедуру CalcNormals я оставил в исходном тексте, но закоментировал.



ReadLn(f, S); //Пропускаем строку "end faces"
ReadLn(f, S); // Пропускаем строку "Faset normals"

// фасетные нормали
for i := 0 to FacesCount - 1 do
begin
  Readln(f,Normal.x,Normal.y,Normal.z);
  FasetNormals[i] := Normal;
end;




Естественно, что количество фасетных нормалей равняется количеству граней.

Загрузка сглаживающих нормалей из файла GMS

Все-таки, несмотря на то, что объект теперь отображается правильно, хочется чего-то еще. Ну кому понравится граненая баранка? Или футбольный мяч такой, будто его вытесали из гранита? И, несмотря на то, что уровень детализации в данном примере не высок, можно еще улучшить внешний вид объекта. На помощь приходят сглаживающие нормали. Об этом стоит рассказать подробнее.

Когда я понял, что, используя команду glShadeModel, мне не удастся сгладить мой объект (и у Вас не получится тоже), я затосковал. Нужно было что-то делать, и я решил заняться этим вопросом вплотную. Вот что мне удалось выяснить. Оказывается к одной грани можно построить не одну нормаль, а столько, сколько душа пожелает. Но это еще ничего не дает. А вот если мы нормаль отклоним в сторону, так что она станет, не перпендикулярна грани, то грань окрасится неравномерно. Конечно, слова о том, что "нормаль не перпендикулярна", могут показаться немного странными для математика, но программиста это смущать не должно :). Я попробую пояснить подробнее, что же получается в этом случае.

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

Это можно применять следующим образом. Чтобы добиться эффекта сглаживания, строить нормали нужно к вершинам грани, на каждую вершину по одной нормали. Для построения нормали, необходимо узнать к каким граням принадлежит вершина (теоретически вершина может принадлежать бесконечному числу граней - на практике не больше 12), взять фасетные нормали от этих граней, расчитать от них среднюю нормаль и построить ее к вершине. Как это сделать? Какими формулами это считается? Честно говоря, я понятия не имею. Есть такой сайт: http://www.pobox.com/~nate Ната Робинсона, там лежит пример на сглаживание и не только. Правда, написан он на Сях. Мне бы не составило труда переписать его на Дельфи, но... Зачем утруждать себя, если есть Баунти? Снова берем 3D Studio Max, лезем внутрь, хватаем сглаживающие нормали и... Вуаля!

Проект находится в папке Ch04. Скомпилируйте его и запустите на выполнение. Теперь Вы можете наслаждаться внешним видом сглаженного бублика нажав на кнопку Фасеты/Сгладить. Выглядит это примерно так:

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



ReadLn(f,S); // Пропускаем строку end faset normals
ReadLn(f,S); // Пропускаем строку SmoothNormals:

// Считываем сглаживающие нормали
for i := 0 to NextMesh.VertexCount - 1 do
begin
  Readln(f,Normal.x,Normal.y,Normal.z);
  NextMesh.SmoothNormals[i] := Normal;
end;




Процедура отрисовки претерпела "существенные" изменения:



procedure TGLMesh.Draw(Smooth: Boolean);
var
  i : Integer;
  Face : TGLFace;
begin
  for i := 0 to FacesCount - 1 do
  begin
    glBegin(GL_TRIANGLES);
    Face := Faces[i];
    if Smooth then
    begin
      // Если сглаживать тогда перед каждой
      glNormal3fv(@SmoothNormals[Face[0]]);
      // вершиной рисуем сглаживающую нормаль
      glVertex3fv(@Vertices[Face[0]]);
      glNormal3fv(@SmoothNormals[Face[1]]);
      glVertex3fv(@Vertices[Face[1]]);
      glNormal3fv(@SmoothNormals[Face[2]]);
      glVertex3fv(@Vertices[Face[2]]);
    end
    else
    // Если не сглаживать один раз рисуем фасетную нормаль
    begin
      glNormal3fv(@FasetNormals[i]);
      glVertex3fv(@Vertices[Face[0]]);
      glVertex3fv(@Vertices[Face[1]]);
      glVertex3fv(@Vertices[Face[2]]);
    end;
    glEnd;
  end;
end;

procedure TGLMultyMesh.Draw;
begin
  if Extent then
  begin
    fExtent := TGLMesh(Meshes.Items[CurrentFrame]).fExtent;
    glScalef(fExtent,fExtent,fExtent);
  end;
  TGLMesh(Meshes.Items[CurrentFrame]).Draw(fSmooth);
  if Action then
  begin
    inc(CurrentFrame);
    if CurrentFrame > (Meshes.Count - 1) then
      CurrentFrame := 0;
  end;
end;




Сам объект TGLMesh дополнился массивом для сглаживающих нормалей, а TGLMultyMesh - флагом указывающим следует ли сглаживать или нет. Этот флаг передается в процедуру отрисовки объекта TGLMesh. Деструктор пополнился строкой уничтожающей массив сглаживающих нормалей. В модуле frmMain появился обработчик нажатия пункта меню Фасеты/Сгладить.

Вот, пожалуй, и все. Могу только добавить, что не всегда удобно пользоваться сглаживающими нормалями из файла GMS, хотя в большинстве случаев они подходят. Загрузите, к примеру, объект Zban.gms и установите сглаживающий режим. Видите, все сглажено, а в 3D Studio Max он выглядел по-другому. Сверху и снизу у него были полукруглые крышки, но посередине был четкий цилиндр, с резкой границей в местах состыковки с полукруглыми крышками. Это побочный эффект сглаживания. Если Вы хотите добится исчезновения этого эффекта, Вам придется написать приложение для ручной корректировки нормалей, или программно отслеживать ситуацию, когда излом достиг критического угла и следует воспользоваться фасетной нормалью. Теперь, пожалуй, действительно все.

Взято с





Экспорт из TDBGrid в Excel без OLE


Экспорт из TDBGrid в Excel без OLE





  Exporting a DBGrid to excel without OLE 

  I develop software and about 95% of my work deals with databases. 
  I enjoied the advantages of using Microsoft Excel in my projects 
  in order to make reports but recently I decided to convert myself 
  to the free OpenOffice suite. 
  I faced with the problem of exporting data to Excel without having 
  Office installed on my computer. 
  The first solution was to create directly an Excel format compatible file: 
  this solution is about 50 times faster than the OLE solution but there 
  is a problem: the output file is not compatible with OpenOffice. 
  I wanted a solution which was compatible with each "DataSet"; 
  at the same time I wanted to export only the dataset data present in 
  a DBGrid and not all the "DataSet". 
  Finally I obtained this solution which satisfied my requirements. 
  I hope that it will be usefull for you too. 

  First of all you must import the ADOX type library 
  which will be used to create the Excel file and its 
  internal structure: in the Delphi IDE: 

  1)Project->Import Type Library: 
  2)Select "Microsoft ADO Ext. for DDL and Security" 
  3)Uncheck "Generate component wrapper" at the bottom 
  4)Rename the class names (TTable, TColumn, TIndex, TKey, TGroup, TUser, TCatalog) in 
    (TXTable, TXColumn, TXIndex, TXKey, TXGroup, TXUser, TXCatalog) 
    in order to avoid conflicts with the already present TTable component. 
  5)Select the Unit dir name and press "Create Unit". 
    It will be created a file named AOX_TLB. 
    Include ADOX_TLB in the "uses" directive inside the file in which you want 
    to use ADOX functionality. 

  That is all. Let's go now with the implementation: 


unit DBGridExportToExcel; 

interface 

uses 
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, 
  ExtCtrls, StdCtrls, ComCtrls, DB, IniFiles, Buttons, dbgrids, ADOX_TLB, ADODB; 


type TScrollEvents = class 
       BeforeScroll_Event: TDataSetNotifyEvent; 
       AfterScroll_Event: TDataSetNotifyEvent; 
       AutoCalcFields_Property: Boolean; 
  end

procedure DisableDependencies(DataSet: TDataSet; var ScrollEvents: TScrollEvents); 
procedure EnableDependencies(DataSet: TDataSet; ScrollEvents: TScrollEvents); 
procedure DBGridToExcelADO(DBGrid: TDBGrid; FileName: string; SheetName: string); 


implementation 

//Support procedures: I made that in order to increase speed in 
//the process of scanning large amounts 
//of records in a dataset 

//we make a call to the "DisableControls" procedure and then disable the "BeforeScroll" and 
//"AfterScroll" events and the "AutoCalcFields" property. 
procedure DisableDependencies(DataSet: TDataSet; var ScrollEvents: TScrollEvents);
begin
  with DataSet do
    begin
      DisableControls;
      ScrollEvents := TScrollEvents.Create();
      with ScrollEvents do
        begin
          BeforeScroll_Event := BeforeScroll;
          AfterScroll_Event := AfterScroll;
          AutoCalcFields_Property := AutoCalcFields;
          BeforeScroll := nil;
          AfterScroll := nil;
          AutoCalcFields := False;
        end;
    end;
end;

//we make a call to the "EnableControls" procedure and then restore
// the "BeforeScroll" and "AfterScroll" events and the "AutoCalcFields" property.

procedure EnableDependencies(DataSet: TDataSet; ScrollEvents: TScrollEvents);
begin
  with DataSet do
    begin
      EnableControls;
      with ScrollEvents do
        begin
          BeforeScroll := BeforeScroll_Event;
          AfterScroll := AfterScroll_Event;
          AutoCalcFields := AutoCalcFields_Property;
        end;
    end;
end;

//This is the procedure which make the work: 

procedure DBGridToExcelADO(DBGrid: TDBGrid; FileName: string; SheetName: string); 
var 
  cat: _Catalog; 
  tbl: _Table; 
  col: _Column; 
  i: integer; 
  ADOConnection: TADOConnection; 
  ADOQuery: TADOQuery; 
  ScrollEvents: TScrollEvents; 
  SavePlace: TBookmark; 
begin 
  // 
  //WorkBook creation (database) 
  cat := CoCatalog.Create; 
  cat._Set_ActiveConnection('Provider=Microsoft.Jet.OLEDB.4.0; Data Source=' + FileName + ';Extended Properties=Excel 8.0'); 
  //WorkSheet creation (table) 
  tbl := CoTable.Create; 
  tbl.Set_Name(SheetName); 
  //Columns creation (fields) 
  DBGrid.DataSource.DataSet.First; 
  with DBGrid.Columns do 
    begin 
      for i := 0 to Count - 1 do 
        if Items[i].Visible then 
        begin 
          col := nil
          col := CoColumn.Create; 
          with col do 
            begin 
              Set_Name(Items[i].Title.Caption); 
              Set_Type_(adVarWChar); 
            end
          //add column to table 
          tbl.Columns.Append(col, adVarWChar, 20); 
        end
    end
  //add table to database 
  cat.Tables.Append(tbl); 

  col := nil
  tbl := nil
  cat := nil

  //exporting 
  ADOConnection := TADOConnection.Create(nil); 
  ADOConnection.LoginPrompt := False; 
  ADOConnection.ConnectionString := 'Provider=Microsoft.Jet.OLEDB.4.0; Data Source=' + FileName + ';Extended Properties=Excel 8.0'; 
  ADOQuery := TADOQuery.Create(nil); 
  ADOQuery.Connection := ADOConnection; 
  ADOQuery.SQL.Text := 'Select * from [' + SheetName + '$]'; 
  ADOQuery.Open; 


  DisableDependencies(DBGrid.DataSource.DataSet, ScrollEvents); 
  SavePlace := DBGrid.DataSource.DataSet.GetBookmark; 
  try 
  with DBGrid.DataSource.DataSet do 
    begin 
      First; 
      while not Eof do 
        begin 
          ADOQuery.Append; 
          with DBGrid.Columns do 
            begin 
              ADOQuery.Edit; 
              for i := 0 to Count - 1 do 
                if Items[i].Visible then 
                  begin 
                    ADOQuery.FieldByName(Items[i].Title.Caption).AsString := FieldByName(Items[i].FieldName).AsString; 
                  end
              ADOQuery.Post; 
            end
          Next; 
        end
    end

  finally 
  DBGrid.DataSource.DataSet.GotoBookmark(SavePlace); 
  DBGrid.DataSource.DataSet.FreeBookmark(SavePlace); 
  EnableDependencies(DBGrid.DataSource.DataSet, ScrollEvents); 

  ADOQuery.Close; 
  ADOConnection.Close; 

  ADOQuery.Free; 
  ADOConnection.Free; 

  end

end

end


Взято с сайта



Элемент управления Edit, реагирующий на событие OnTimer.


Элемент управления Edit, реагирующий на событие OnTimer.



Как-то раз встала такая проблема: если пользователь какое-то время ничего не вводит в элемент управления Edit, то предупредить его об этом.

unit EditOnTime; 

interface 

uses 
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, ExtCtrls, StdCtrls; 

type 
TEditOnTime = class(TEdit) 
private 
  FInterval: integer; 
  FTimer: TTimer; 
  FOnTimer: TNotifyEvent; 
  procedure SetInterval(Interval: integer); 
  procedure Timer(Sender: TObject); 
protected 
  procedure KeyPress(var Key: char); override; 
public 
  constructor Create(AOwner: TComponent); override; 
  destructor Destroy; override; 
published 
  property Interval: integer read FInterval write SetInterval default 750; 
property OnTimer: TNotifyEvent read FOnTimer write FOnTimer;   
end; 

procedure Register; 

implementation 

//******************* RegisterComponent 
// Здесь мы регистрируем компонент в IDE 
procedure Register; 
begin 
RegisterComponents('MPS', [TEditOnTime]);   
end; 

//******************* TEditOnTime.SetInterval 
// устанавливаем интервал таймера 
procedure TEditOnTime.SetInterval(Interval: integer); 
begin 
FInterval := Interval;   
if Assigned(FTimer) then   
  FTimer.Interval := FInterval;   
end; 

//******************* TEditOnTime.Create 
constructor TEditOnTime.Create(AOwner: TComponent); 
begin 
FInterval := 750;   
inherited Create(AOwner);   
if not (csDesigning in ComponentState) then   
  try   
   FTimer := TTimer.Create(self);   
   FTimer.Enabled := false;   
   FTimer.Interval := FInterval;   
   FTimer.OnTimer := Timer;   
  except   
   FreeAndNil(FTimer);   
  end;   
end; 

//******************* TEditOnTime.Destroy 
destructor TEditOnTime.Destroy; 
begin 
if Assigned(FTimer) then FreeAndNil(FTimer);   
inherited Destroy;   
end; 

//******************* TEditOnTime.OnTimer 
procedure TEditOnTime.Timer(Sender: TObject); 
begin 
FTimer.Enabled := false;   
if Assigned(FOnTimer) then FOnTimer(self);   
end; 

//******************* TEditOnTime.KeyPress 
procedure TEditOnTime.KeyPress(var Key: char); 
begin 
FTimer.Enabled := false;   
inherited KeyPress(Key);   
FTimer.Enabled := (Text <> '') and Assigned(FTimer) and Assigned(FOnTimer);   
end; 

end.

Взято с Исходников.ru



Элементы спектрального анализа (Фурье, Хартман etc.)


Элементы спектрального анализа (Фурье, Хартман etc.)





{$A+,B-,C+,D+,E-,F-,G+,H+,I+,J+,K-,L+,M-,N+,O-,P+,Q-,R-,S-,T-,U-,V+,W-,X+,Y+,Z1}
{$MINSTACKSIZE$00004000}
{$MAXSTACKSIZE $00100000}
{$IMAGEBASE $00400000}
{$APPTYPE GUI}

unit Main;

interface

uses

  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  Buttons, ExtCtrls, ComCtrls, Menus;

type

  TfmMain = class(TForm)
    MainMenu1: TMainMenu;
    N1: TMenuItem;
    N2: TMenuItem;
    StatusBar1: TStatusBar;
    N3: TMenuItem;
    imgInfo: TImage;
    Panel1: TPanel;
    btnStart: TSpeedButton;
    procedure btnStartClick(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
  end;

var

  fmMain: TfmMain;

implementation

uses PFiles;

{$R *.DFM}

function Power2(lPower: Byte): LongInt;

begin
  Result := 1 shl lPower;
end;

procedure ClassicDirect(var aSignal, aSpR, aSpI: array of Double; N:
  LongInt);

var lSrch: LongInt;
var lGarm: LongInt;
var dSumR: Double;
var dSumI: Double;
begin
  for lGarm := 0 to N div 2 - 1 do
    begin
      dSumR := 0;
      dSumI := 0;
      for lSrch := 0 to N - 1 do
        begin
          dSumR := dSumR + aSignal[lSrch] * Cos(lGarm * lSrch / N * 2 * PI);
          dSumI := dSumI + aSignal[lSrch] * Sin(lGarm * lSrch / N * 2 * PI);
        end;
      aSpR[lGarm] := dSumR;
      aSpI[lGarm] := dSumI;
    end;
end;

procedure ClassicInverce(var aSpR, aSpI, aSignal: array of Double; N:
  LongInt);

var lSrch: LongInt;
var lGarm: LongInt;
var dSum: Double;
begin
  for lSrch := 0 to N - 1 do
    begin
      dSum := 0;
      for lGarm := 0 to N div 2 - 1 do
        dSum := dSum
          + aSpR[lGarm] * Cos(lSrch * lGarm * 2 * Pi / N)
          + aSpI[lGarm] * Sin(lSrch * lGarm * 2 * Pi / N);
      aSignal[lSrch] := dSum * 2;
    end;
end;

function InvertBits(BF, DataSize, Power: Word): Word;

var BR: Word;
var NN: Word;
var L: Word;
begin
  br := 0;
  nn := DataSize;
  for l := 1 to Power do
    begin
      NN := NN div 2;
      if (BF >= NN) then
        begin
          BR := BR + Power2(l - 1);
          BF := BF - NN
        end;
    end;
  InvertBits := BR;
end;

procedure FourierDirect(var RealData, VirtData, ResultR, ResultV: array of
  Double; DataSize: LongInt);

var A1: Real;
var A2: Real;
var B1: Real;
var B2: Real;
var D2: Word;
var C2: Word;
var C1: Word;
var D1: Word;
var I: Word;
var J: Word;
var K: Word;
var Cosin: Real;
var Sinus: Real;
var wIndex: Word;
var Power: Word;
begin
  C1 := DataSize shr 1;
  C2 := 1;
  for Power := 0 to 15 //hope it will be faster then
    round(ln(DataSize) / ln(2)) do
    if Power2(Power) = DataSize then Break;
  for I := 1 to Power do
    begin
      D1 := 0;
      D2 := C1;
      for J := 1 to C2 do
        begin
          wIndex := InvertBits(D1 div C1, DataSize, Power);
          Cosin := +(Cos((2 * Pi / DataSize) * wIndex));
          Sinus := -(Sin((2 * Pi / DataSize) * wIndex));
          for K := D1 to D2 - 1 do
            begin
              A1 := RealData[K];
              A2 := VirtData[K];
              B1 := ((Cosin * RealData[K + C1] - Sinus * VirtData[K + C1]));
              B2 := ((Sinus * RealData[K + C1] + Cosin * VirtData[K + C1]));
              RealData[K] := A1 + B1;
              VirtData[K] := A2 + B2;
              RealData[K + C1] := A1 - B1;
              VirtData[K + C1] := A2 - B2;
            end;
          Inc(D1, C1 * 2);
          Inc(D2, C1 * 2);
        end;
      C1 := C1 div 2;
      C2 := C2 * 2;
    end;
  for I := 0 to DataSize div 2 - 1 do
    begin
      ResultR[I] := +RealData[InvertBits(I, DataSize, Power)];
      ResultV[I] := -VirtData[InvertBits(I, DataSize, Power)];
    end;
end;

procedure Hartley(iSize: LongInt; var aData: array of Double);

type taDouble = array[0..MaxLongInt div SizeOf(Double) - 1] of Double;
var prFI, prFN, prGI: ^taDouble;
var rCos, rSin: Double;
var rA, rB, rTemp: Double;
var rC1, rC2, rC3, rC4: Double;
var rS1, rS2, rS3, rS4: Double;
var rF0, rF1, rF2, rF3: Double;
var rG0, rG1, rG2, rG3: Double;
var iK1, iK2, iK3, iK4: LongInt;
var iSrch, iK, iKX: LongInt;
begin
  iK2 := 0;
  for iK1 := 1 to iSize - 1 do
    begin
      iK := iSize shr 1;
      repeat
        iK2 := iK2 xor iK;
        if (iK2 and iK) <> 0 then Break;
        iK := iK shr 1;
      until False;
      if iK1 > iK2 then
        begin
          rTemp := aData[iK1];
          aData[iK1] := aData[iK2];
          aData[iK2] := rTemp;
        end;
    end;
  iK := 0;
  while (1 shl iK) < iSize do
    Inc(iK);
  iK := iK and 1;
  if iK = 0 then
    begin
      prFI := @aData;
      prFN := @aData;
      prFN := @prFN[iSize];
      while Word(prFI) < Word(prFN) do
        begin
          rF1 := prFI^[0] - prFI^[1];
          rF0 := prFI^[0] + prFI^[1];
          rF3 := prFI^[2] - prFI^[3];
          rF2 := prFI^[2] + prFI^[3];
          prFI^[2] := rF0 - rF2;
          prFI^[0] := rF0 + rF2;
          prFI^[3] := rF1 - rF3;
          prFI^[1] := rF1 + rF3;
          prFI := @prFI[4];
        end;
    end
  else
    begin
      prFI := @aData;
      prFN := @aData;
      prFN := @prFN[iSize];
      prGI := prFI;
      prGI := @prGI[1];
      while Word(prFI) < Word(prFN) do
        begin
          rC1 := prFI^[0] - prGI^[0];
          rS1 := prFI^[0] + prGI^[0];
          rC2 := prFI^[2] - prGI^[2];
          rS2 := prFI^[2] + prGI^[2];
          rC3 := prFI^[4] - prGI^[4];
          rS3 := prFI^[4] + prGI^[4];
          rC4 := prFI^[6] - prGI^[6];
          rS4 := prFI^[6] + prGI^[6];
          rF1 := rS1 - rS2;
          rF0 := rS1 + rS2;
          rG1 := rC1 - rC2;
          rG0 := rC1 + rC2;
          rF3 := rS3 - rS4;
          rF2 := rS3 + rS4;
          rG3 := Sqrt(2) * rC4;
          rG2 := Sqrt(2) * rC3;
          prFI^[4] := rF0 - rF2;
          prFI^[0] := rF0 + rF2;
          prFI^[6] := rF1 - rF3;
          prFI^[2] := rF1 + rF3;
          prGI^[4] := rG0 - rG2;
          prGI^[0] := rG0 + rG2;
          prGI^[6] := rG1 - rG3;
          prGI^[2] := rG1 + rG3;
          prFI := @prFI[8];
          prGI := @prGI[8];
        end;
    end;
  if iSize < 16 then Exit;
  repeat
    Inc(iK, 2);
    iK1 := 1 shl iK;
    iK2 := iK1 shl 1;
    iK4 := iK2 shl 1;
    iK3 := iK2 + iK1;
    iKX := iK1 shr 1;
    prFI := @aData;
    prGI := prFI;
    prGI := @prGI[iKX];
    prFN := @aData;
    prFN := @prFN[iSize];
    repeat
      rF1 := prFI^[000] - prFI^[iK1];
      rF0 := prFI^[000] + prFI^[iK1];
      rF3 := prFI^[iK2] - prFI^[iK3];
      rF2 := prFI^[iK2] + prFI^[iK3];
      prFI^[iK2] := rF0 - rF2;
      prFI^[000] := rF0 + rF2;
      prFI^[iK3] := rF1 - rF3;
      prFI^[iK1] := rF1 + rF3;
      rG1 := prGI^[0] - prGI^[iK1];
      rG0 := prGI^[0] + prGI^[iK1];
      rG3 := Sqrt(2) * prGI^[iK3];
      rG2 := Sqrt(2) * prGI^[iK2];
      prGI^[iK2] := rG0 - rG2;
      prGI^[000] := rG0 + rG2;
      prGI^[iK3] := rG1 - rG3;
      prGI^[iK1] := rG1 + rG3;
      prGI := @prGI[iK4];
      prFI := @prFI[iK4];
    until not (Word(prFI) < Word(prFN));
    rCos := Cos(Pi / 2 / Power2(iK));
    rSin := Sin(Pi / 2 / Power2(iK));
    rC1 := 1;
    rS1 := 0;
    for iSrch := 1 to iKX - 1 do
      begin
        rTemp := rC1;
        rC1 := (rTemp * rCos - rS1 * rSin);
        rS1 := (rTemp * rSin + rS1 * rCos);
        rC2 := (rC1 * rC1 - rS1 * rS1);
        rS2 := (2 * (rC1 * rS1));
        prFN := @aData;
        prFN := @prFN[iSize];
        prFI := @aData;
        prFI := @prFI[iSrch];
        prGI := @aData;
        prGI := @prGI[iK1 - iSrch];
        repeat
          rB := (rS2 * prFI^[iK1] - rC2 * prGI^[iK1]);
          rA := (rC2 * prFI^[iK1] + rS2 * prGI^[iK1]);
          rF1 := prFI^[0] - rA;
          rF0 := prFI^[0] + rA;
          rG1 := prGI^[0] - rB;
          rG0 := prGI^[0] + rB;
          rB := (rS2 * prFI^[iK3] - rC2 * prGI^[iK3]);
          rA := (rC2 * prFI^[iK3] + rS2 * prGI^[iK3]);
          rF3 := prFI^[iK2] - rA;
          rF2 := prFI^[iK2] + rA;
          rG3 := prGI^[iK2] - rB;
          rG2 := prGI^[iK2] + rB;
          rB := (rS1 * rF2 - rC1 * rG3);
          rA := (rC1 * rF2 + rS1 * rG3);
          prFI^[iK2] := rF0 - rA;
          prFI^[0] := rF0 + rA;
          prGI^[iK3] := rG1 - rB;
          prGI^[iK1] := rG1 + rB;
          rB := (rC1 * rG2 - rS1 * rF3);
          rA := (rS1 * rG2 + rC1 * rF3);
          prGI^[iK2] := rG0 - rA;
          prGI^[0] := rG0 + rA;
          prFI^[iK3] := rF1 - rB;
          prFI^[iK1] := rF1 + rB;
          prGI := @prGI[iK4];
          prFI := @prFI[iK4];
        until not (LongInt(prFI) < LongInt(prFN));
      end;
  until not (iK4 < iSize);
end;

procedure HartleyDirect(
  var aData: array of Double;

  iSize: LongInt);
var rA, rB: Double;
var iI, iJ, iK: LongInt;
begin
  Hartley(iSize, aData);
  iJ := iSize - 1;
  iK := iSize div 2;
  for iI := 1 to iK - 1 do
    begin
      rA := aData[ii];
      rB := aData[ij];
      aData[iJ] := (rA - rB) / 2;
      aData[iI] := (rA + rB) / 2;
      Dec(iJ);
    end;
end;

procedure HartleyInverce(
  var aData: array of Double;

  iSize: LongInt);

var rA, rB: Double;
var iI, iJ, iK: LongInt;
begin
  iJ := iSize - 1;
  iK := iSize div 2;
  for iI := 1 to iK - 1 do
    begin
      rA := aData[iI];
      rB := aData[iJ];
      aData[iJ] := rA - rB;
      aData[iI] := rA + rB;
      Dec(iJ);
    end;
  Hartley(iSize, aData);
end;

//not tested

procedure HartleyDirectComplex(real, imag: array of Double; n: LongInt);
var a, b, c, d: double;

  q, r, s, t: double;
  i, j, k: LongInt;
begin

  j := n - 1;
  k := n div 2;
  for i := 1 to k - 1 do
    begin
      a := real[i]; b := real[j]; q := a + b; r := a - b;
      c := imag[i]; d := imag[j]; s := c + d; t := c - d;
      real[i] := (q + t) * 0.5; real[j] := (q - t) * 0.5;
      imag[i] := (s - r) * 0.5; imag[j] := (s + r) * 0.5;
      dec(j);
    end;
  Hartley(N, Real);
  Hartley(N, Imag);
end;

//not tested

procedure HartleyInverceComplex(real, imag: array of Double; N: LongInt);
var a, b, c, d: double;

  q, r, s, t: double;
  i, j, k: longInt;
begin
  Hartley(N, real);
  Hartley(N, imag);
  j := n - 1;
  k := n div 2;
  for i := 1 to k - 1 do
    begin
      a := real[i]; b := real[j]; q := a + b; r := a - b;
      c := imag[i]; d := imag[j]; s := c + d; t := c - d;
      imag[i] := (s + r) * 0.5; imag[j] := (s - r) * 0.5;
      real[i] := (q - t) * 0.5; real[j] := (q + t) * 0.5;
      dec(j);
    end;
end;

procedure DrawSignal(var aSignal: array of Double; N, lColor: LongInt);

var lSrch: LongInt;
var lHalfHeight: LongInt;
begin
  with fmMain do
    begin
      lHalfHeight := imgInfo.Height div 2;
      imgInfo.Canvas.MoveTo(0, lHalfHeight);
      imgInfo.Canvas.Pen.Color := lColor;
      for lSrch := 0 to N - 1 do
        begin
          imgInfo.Canvas.LineTo(lSrch, Round(aSignal[lSrch]) + lHalfHeight);
        end;
      imgInfo.Repaint;
    end;
end;

procedure DrawSpector(var aSpR, aSpI: array of Double; N, lColR, lColI:
  LongInt);

var lSrch: LongInt;
var lHalfHeight: LongInt;
begin
  with fmMain do
    begin
      lHalfHeight := imgInfo.Height div 2;
      for lSrch := 0 to N div 2 do
        begin
          imgInfo.Canvas.Pixels[lSrch, Round(aSpR[lSrch] / N) + lHalfHeight] :=
            lColR;

          imgInfo.Canvas.Pixels[lSrch + N div 2, Round(aSpI[lSrch] / N) +
            lHalfHeight] := lColI;

        end;
      imgInfo.Repaint;
    end;
end;

const N = 512;
var aSignalR: array[0..N - 1] of Double; //
var aSignalI: array[0..N - 1] of Double; //
var aSpR, aSpI: array[0..N div 2 - 1] of Double; //
var lFH: LongInt;

procedure TfmMain.btnStartClick(Sender: TObject);

const Epsilon = 0.00001;
var lSrch: LongInt;
var aBuff: array[0..N - 1] of ShortInt;
begin
  if lFH > 0 then
    begin
//   Repeat

      if F.Read(lFH, @aBuff, N) <> N then
        begin
          Exit;
        end;
      for lSrch := 0 to N - 1 do
        begin
          aSignalR[lSrch] := ShortInt(aBuff[lSrch] + $80);
          aSignalI[lSrch] := 0;
        end;

      imgInfo.Canvas.Rectangle(0, 0, imgInfo.Width, imgInfo.Height);
      DrawSignal(aSignalR, N, $D0D0D0);

//    ClassicDirect(aSignalR, aSpR, aSpI, N);                 //result in aSpR & aSpI,
      aSignal unchanged
//    FourierDirect(aSignalR, aSignalI, aSpR, aSpI, N);       //result in aSpR &
      aSpI, aSiggnalR & aSignalI modified

      HartleyDirect(aSignalR, N); //result in source aSignal ;-)

      DrawSpector(aSignalR, aSignalR[N div 2 - 1], N, $80, $8000);
      DrawSpector(aSpR, aSpI, N, $80, $8000);

{    for lSrch := 0 to N div 2 -1 do begin                    //comparing classic & Hartley

if (Abs(aSpR[lSrch] - aSignal[lSrch]) > Epsilon)
or ((lSrch > 0) And (Abs(aSpI[lSrch] - aSignal[N - lSrch]) > Epsilon))
then MessageDlg('Error comparing',mtError,[mbOK],-1);
end;}

      HartleyInverce(aSignalR, N); //to restore original signal with
      HartleyDirect
//    ClassicInverce(aSpR, aSpI, aSignalR, N);                //to restore original
      signal with ClassicDirect or FourierDirect

      for lSrch := 0 to N - 1 do
        aSignalR[lSrch] := aSignalR[lSrch] / N; //scaling

      DrawSignal(aSignalR, N, $D00000);
      Application.ProcessMessages;
//   Until False;

    end;
end;

procedure TfmMain.FormCreate(Sender: TObject);

begin
  lFH := F.Open('input.pcm', ForRead);
end;

procedure TfmMain.FormClose(Sender: TObject; var Action: TCloseAction);

begin
  F.Close(lFH);
end;

end.

Denis Furman [000705

Взято из

Советов по Delphi от


Сборник Kuliba


Привожу FFT-алгоритм, позволяющий оперировать 256 точками данных примерно за 0.008 секунд на P66 (с 72MB, YMMV). Создан на Delphi.

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

Но я не думаю что алгоритм слишком плох, в нем заложено немало математических трюков. Имеется некоторое количество рекурсий, но они занимается не копированием данных, а манипуляциями с указателями, если у нас есть массив размером N = 2^d, то глубина рекурсии составит всего d. Возможно имело бы смысл применить развертывающуюся рекурсию, но не пока не ясно, поможет ли ее применение в данном алгоритме. (Но вероятно мы смогли бы достаточно легко получить надежную математическую модель, развертывая в рекурсии один или два нижних слоя, то есть проще говоря:



if Depth < 2 then
{производим какие-либо действия}




вместо текущего 'if Depth = 0 then...' Это должно устранить непродуктивные вызовы функций, что несомненно хорошо в то время, пока развертывающая рекурсия работает с ресурсами.)

Имеется поиск с применением таблиц синусов и косинусов; здесь использован метод золотой середины: данный алгоритм весьма трудоемок, но дает отличные результаты при использовании малых и средних массивов.

Вероятно в машине с большим объемом оперативной памяти следует использовать VirtualAlloc(... PAGE_NOCACHE) для Src, Dest и таблиц поиска.

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

Что делает данная технология вкратце. Имеется несколько FFT, образующих 'комплексный FT', который понимает и о котором заботится моя технология. Это означает, что если N = 2^d, Src^ и Dest^ образуют массив из N TComplexes, происходит вызов



FFT(d, Src, Dest)




, далее заполняем Dest с применением 'комплексного FT' после того, как результат вызова Dest^[j] будет равен



1/sqrt(N) * Sum(k=0.. N - 1 ; EiT(2*Pi(j*k/N)) * Src^[k])




, где EiT(t) = cos(t) + i sin(t) . То есть, стандартное преобразование Фурье.

Публикую две версии: в первой версии я использую TComplex с функциями для работы с комплексными числами. Во второй версии все числа реальные - вместо массивов Src и Dest мы используем массивы реальных чисел SrcR, SrcI, DestR, DestI (в блоке вычислений реальных чисел), и вызовы всех функций осуществляются линейно. Первая версия достаточна легка в реализации, зато вторая - значительно быстрее. (Обе версии оперируют 'комплексными FFT'.) Технология работы была опробована на алгоритме Plancherel (также известным как Parseval). Обе версии работоспособны, btw: если это не работает у вас - значит я что-то выбросил вместе со своими глупыми коментариями :-) Итак, сложная версия:



unitcplx;

interface

type

  PReal = ^TReal;
  TReal = extended;

  PComplex = ^TComplex;
  TComplex = record
    r: TReal;
    i: TReal;
  end;

function MakeComplex(x, y: TReal): TComplex;
function Sum(x, y: TComplex): TComplex;
function Difference(x, y: TComplex): TComplex;
function Product(x, y: TComplex): TComplex;
function TimesReal(x: TComplex; y: TReal): TComplex;
function PlusReal(x: TComplex; y: TReal): TComplex;
function EiT(t: TReal): TComplex;
function ComplexToStr(x: TComplex): string;
function AbsSquared(x: TComplex): TReal;

implementation

uses SysUtils;

function MakeComplex(x, y: TReal): TComplex;
begin

  with result do
  begin
    r := x;
    i := y;
  end;
end;

function Sum(x, y: TComplex): TComplex;
begin
  with result do
  begin

    r := x.r + y.r;
    i := x.i + y.i;
  end;
end;

function Difference(x, y: TComplex): TComplex;
begin
  with result do
  begin

    r := x.r - y.r;
    i := x.i - y.i;
  end;
end;

function EiT(t: TReal): TComplex;
begin
  with result do
  begin

    r := cos(t);
    i := sin(t);
  end;
end;

function Product(x, y: TComplex): TComplex;
begin
  with result do
  begin

    r := x.r * y.r - x.i * y.i;
    i := x.r * y.i + x.i * y.r;
  end;
end;

function TimesReal(x: TComplex; y: TReal): TComplex;
begin
  with result do
  begin

    r := x.r * y;
    i := x.i * y;
  end;
end;

function PlusReal(x: TComplex; y: TReal): TComplex;
begin
  with result do
  begin

    r := x.r + y;
    i := x.i;
  end;
end;

function ComplexToStr(x: TComplex): string;
begin
  result := FloatToStr(x.r)
    + ' + '
    + FloatToStr(x.i)
    + 'i';
end;

function AbsSquared(x: TComplex): TReal;
begin
  result := x.r * x.r + x.i * x.i;
end;

end.





unit cplxfft1;

interface

uses Cplx;

type
  PScalar = ^TScalar;
  TScalar = TComplex; {Легко получаем преобразование в реальную величину}

  PScalars = ^TScalars;
  TScalars = array[0..High(integer) div SizeOf(TScalar) - 1]
    of TScalar;

const
  TrigTableDepth: word = 0;
  TrigTable: PScalars = nil;

procedure InitTrigTable(Depth: word);

procedure FFT(Depth: word;
  Src: PScalars;
  Dest: PScalars);

{Перед вызовом Src и Dest ТРЕБУЕТСЯ распределение
(integer(1) shl Depth) * SizeOf(TScalar)
байт памяти!}

implementation

procedure DoFFT(Depth: word;
  Src: PScalars;
  SrcSpacing: word;
  Dest: PScalars);
{рекурсивная часть, вызываемая при готовности FFT}
var
  j, N: integer;
  Temp: TScalar;
  Shift: word;
begin
  if Depth = 0 then
  begin
    Dest^[0] := Src^[0];
    exit;
  end;

  N := integer(1) shl (Depth - 1);

  DoFFT(Depth - 1, Src, SrcSpacing * 2, Dest);
  DoFFT(Depth - 1, @Src^[SrcSpacing], SrcSpacing * 2, @Dest^[N]);

  Shift := TrigTableDepth - Depth;

  for j := 0 to N - 1 do
  begin
    Temp := Product(TrigTable^[j shl Shift],
      Dest^[j + N]);
    Dest^[j + N] := Difference(Dest^[j], Temp);
    Dest^[j] := Sum(Dest^[j], Temp);
  end;
end;

procedure FFT(Depth: word;
  Src: PScalars;
  Dest: PScalars);
var
  j, N: integer;
  Normalizer: extended;
begin
  N := integer(1) shl depth;
  if Depth TrigTableDepth then
    InitTrigTable(Depth);
  DoFFT(Depth, Src, 1, Dest);
  Normalizer := 1 / sqrt(N);
  for j := 0 to N - 1 do
    Dest^[j] := TimesReal(Dest^[j], Normalizer);
end;

procedure InitTrigTable(Depth: word);
var
  j, N: integer;
begin
  N := integer(1) shl depth;
  ReAllocMem(TrigTable, N * SizeOf(TScalar));
  for j := 0 to N - 1 do

    TrigTable^[j] := EiT(-(2 * Pi) * j / N);
  TrigTableDepth := Depth;
end;

initialization
  ;

finalization
  ReAllocMem(TrigTable, 0);

end.





unit DemoForm;

interface

uses

  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls;

type

  TForm1 = class(TForm)
    Button1: TButton;
    Memo1: TMemo;
    Edit1: TEdit;
    Label1: TLabel;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var

  Form1: TForm1;

implementation

{$R *.DFM}

uses cplx, cplxfft1, MMSystem;

procedure TForm1.Button1Click(Sender: TObject);
var
  j: integer;
  s: string;

  src, dest: PScalars;
  norm: extended;
  d, N, count: integer;
  st, et: longint;
begin

  d := StrToIntDef(edit1.text, -1);
  if d < 1 then
    raise
      exception.Create('глубина рекурсии должны быть положительным целым числом');

  N := integer(1) shl d;

  GetMem(Src, N * Sizeof(TScalar));
  GetMem(Dest, N * SizeOf(TScalar));

  for j := 0 to N - 1 do
  begin
    src^[j] := MakeComplex(random, random);
  end;

  begin

    st := timeGetTime;
    FFT(d, Src, dest);
    et := timeGetTime;

  end;

  Memo1.Lines.Add('N = ' + IntToStr(N));
  Memo1.Lines.Add('норма ожидания: ' + #9 + FloatToStr(N * 2 / 3));

  norm := 0;
  for j := 0 to N - 1 do
    norm := norm + AbsSquared(src^[j]);
  Memo1.Lines.Add('Норма данных: ' + #9 + FloatToStr(norm));
  norm := 0;
  for j := 0 to N - 1 do
    norm := norm + AbsSquared(dest^[j]);
  Memo1.Lines.Add('Норма FT: ' + #9#9 + FloatToStr(norm));

  Memo1.Lines.Add('Время расчета FFT: ' + #9
    + inttostr(et - st)
    + ' мс.');
  Memo1.Lines.Add(' ');

  FreeMem(Src);
  FreeMem(DEst);
end;

end.




**** Версия для работы с реальными числами:



unit cplxfft2;

interface

type

  PScalar = ^TScalar;
  TScalar = extended;

  PScalars = ^TScalars;
  TScalars = array[0..High(integer) div SizeOf(TScalar) - 1]
    of TScalar;

const

  TrigTableDepth: word = 0;
  CosTable: PScalars = nil;
  SinTable: PScalars = nil;

procedure InitTrigTables(Depth: word);

procedure FFT(Depth: word;

  SrcR, SrcI: PScalars;
  DestR, DestI: PScalars);

{Перед вызовом Src и Dest ТРЕБУЕТСЯ распределение

(integer(1) shl Depth) * SizeOf(TScalar)

байт памяти!}

implementation

procedure DoFFT(Depth: word;

  SrcR, SrcI: PScalars;
  SrcSpacing: word;
  DestR, DestI: PScalars);
{рекурсивная часть, вызываемая при готовности FFT}
var
  j, N: integer;

  TempR, TempI: TScalar;
  Shift: word;
  c, s: extended;
begin
  if Depth = 0 then

  begin
    DestR^[0] := SrcR^[0];
    DestI^[0] := SrcI^[0];
    exit;
  end;

  N := integer(1) shl (Depth - 1);

  DoFFT(Depth - 1, SrcR, SrcI, SrcSpacing * 2, DestR, DestI);
  DoFFT(Depth - 1,

    @SrcR^[srcSpacing],
    @SrcI^[SrcSpacing],
    SrcSpacing * 2,
    @DestR^[N],
    @DestI^[N]);

  Shift := TrigTableDepth - Depth;

  for j := 0 to N - 1 do
  begin

    c := CosTable^[j shl Shift];
    s := SinTable^[j shl Shift];

    TempR := c * DestR^[j + N] - s * DestI^[j + N];
    TempI := c * DestI^[j + N] + s * DestR^[j + N];

    DestR^[j + N] := DestR^[j] - TempR;
    DestI^[j + N] := DestI^[j] - TempI;

    DestR^[j] := DestR^[j] + TempR;
    DestI^[j] := DestI^[j] + TempI;
  end;

end;

procedure FFT(Depth: word;

  SrcR, SrcI: PScalars;
  DestR, DestI: PScalars);
var
  j, N: integer;
  Normalizer: extended;
begin

  N := integer(1) shl depth;

  if Depth TrigTableDepth then

    InitTrigTables(Depth);

  DoFFT(Depth, SrcR, SrcI, 1, DestR, DestI);

  Normalizer := 1 / sqrt(N);

  for j := 0 to N - 1 do

  begin
    DestR^[j] := DestR^[j] * Normalizer;
    DestI^[j] := DestI^[j] * Normalizer;
  end;

end;

procedure InitTrigTables(Depth: word);
var
  j, N: integer;
begin

  N := integer(1) shl depth;
  ReAllocMem(CosTable, N * SizeOf(TScalar));
  ReAllocMem(SinTable, N * SizeOf(TScalar));
  for j := 0 to N - 1 do

  begin
    CosTable^[j] := cos(-(2 * Pi) * j / N);
    SinTable^[j] := sin(-(2 * Pi) * j / N);
  end;
  TrigTableDepth := Depth;

end;

initialization

  ;

finalization

  ReAllocMem(CosTable, 0);
  ReAllocMem(SinTable, 0);

end.

 



unit demofrm;

interface

uses

  Windows, Messages, SysUtils, Classes, Graphics,
  Controls, Forms, Dialogs, cplxfft2, StdCtrls;

type

  TForm1 = class(TForm)
    Button1: TButton;
    Memo1: TMemo;
    Edit1: TEdit;
    Label1: TLabel;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var

  Form1: TForm1;

implementation

{$R *.DFM}

uses MMSystem;

procedure TForm1.Button1Click(Sender: TObject);
var
  SR, SI, DR, DI: PScalars;
  j, d, N: integer;
  st, et: longint;
  norm: extended;
begin

  d := StrToIntDef(edit1.text, -1);
  if d < 1 then
    raise
      exception.Create('глубина рекурсии должны быть положительным целым числом');

  N := integer(1) shl d;

  GetMem(SR, N * SizeOf(TScalar));
  GetMem(SI, N * SizeOf(TScalar));
  GetMem(DR, N * SizeOf(TScalar));
  GetMem(DI, N * SizeOf(TScalar));

  for j := 0 to N - 1 do
  begin

    SR^[j] := random;
    SI^[j] := random;
  end;

  st := timeGetTime;
  FFT(d, SR, SI, DR, DI);

  et := timeGetTime;

  memo1.Lines.Add('N = ' + inttostr(N));
  memo1.Lines.Add('норма ожидания: ' + #9 + FloatToStr(N * 2 / 3));

  norm := 0;
  for j := 0 to N - 1 do

    norm := norm + SR^[j] * SR^[j] + SI^[j] * SI^[j];
  memo1.Lines.Add('норма данных: ' + #9 + FloatToStr(norm));

  norm := 0;
  for j := 0 to N - 1 do

    norm := norm + DR^[j] * DR^[j] + DI^[j] * DI^[j];
  memo1.Lines.Add('норма FT: ' + #9#9 + FloatToStr(norm));

  memo1.Lines.Add('Время расчета FFT: ' + #9 + inttostr(et - st));
  memo1.Lines.add('');
  (*for j:=0 to N - 1 do

  Memo1.Lines.Add(FloatToStr(SR^[j])
  + ' + '
  + FloatToStr(SI^[j])
  + 'i');

  for j:=0 to N - 1 do

  Memo1.Lines.Add(FloatToStr(DR^[j])
  + ' + '
  + FloatToStr(DI^[j])
  + 'i');*)

  FreeMem(SR, N * SizeOf(TScalar));
  FreeMem(SI, N * SizeOf(TScalar));
  FreeMem(DR, N * SizeOf(TScalar));
  FreeMem(DI, N * SizeOf(TScalar));
end;

end.



Взято с






JPG ---> BMP


JPG ---> BMP





uses 
  JPEG; 

procedure JPEGtoBMP(const FileName: TFileName); 
var 
  jpeg: TJPEGImage; 
  bmp:  TBitmap; 
begin 
  jpeg := TJPEGImage.Create; 
  try 
    jpeg.CompressionQuality := 100; {Default Value} 
    jpeg.LoadFromFile(FileName); 
    bmp := TBitmap.Create; 
    try 
      bmp.Assign(jpeg); 
      bmp.SaveTofile(ChangeFileExt(FileName, '.bmp')); 
    finally 
      bmp.Free 
    end; 
  finally 
    jpeg.Free 
  end; 
end; 



  CompressionQuality (default 100): 
  Set a value between 1..100, depending on your need of quality and 
  image file size. 1 = Smallest file size, 100 = Best quality. 

  Mit CompressionQuality konnen Sie die Qualitat der Komprimierung fur die 
  JPEG-Grafik festlegen (Default ist 100), wenn diese gespeichert wird. 
  Eine hohere Komprimierung ergibt eine etwas schlechtere Bildqualitat, 
  dafur aber eine kleinere Datei. 
  1 = kleinste Dateigrosse, 100 = beste Qualitat 


Взято с сайта



Качественно уменьшить изображение


Качественно уменьшить изображение




В Delphi изменять размеры изображения очень просто, используя CopyRect:



procedureTForm1.Button1Click(Sender: TObject);
begin
  Form1.Canvas.Font.Size := 24;
  Form1.Canvas.TextOut(0, 0, 'Text');
  Form1.Canvas.CopyRect(Bounds(0, 50, 25, 10), Form1.Canvas,
  Bounds(0, 0, 100, 40));
end;




Но этот способ не очень хорош для уменьшения не маленьких картинок ? мелкие детали сливаются. Для частичного устранения этого недостатка при уменьшении изображения в четыре раза я беру средний цвет в каждом квадратике 4X4. К чему это приводит, посмотрите сами.



procedure TForm1.Button1Click(Sender: TObject);
var
  x, y: integer;
  i, j: integer;
  r, g, b: integer;
begin
  Form1.Canvas.Font.Size := 24;
  Form1.Canvas.TextOut(0, 0, 'Text');
  for y := 0 to 10 do
  begin
    for x := 0 to 25 do
    begin
      r := 0;
      for i := 0 to 3 do
        for j := 0 to 3 do
          r := r + GetRValue(Form1.Canvas.Pixels[4*x+i, 4*y+j]);
      r := round(r / 16);
      g := 0;
      for i := 0 to 3 do
        for j := 0 to 3 do
          g := g + GetGValue(Form1.Canvas.Pixels[4*x+i, 4*y+j]);
      g := round(g / 16);
      b := 0;
      for i := 0 to 3 do
        for j := 0 to 3 do
          b := b + GetBValue(Form1.Canvas.Pixels[4*x+i, 4*y+j]);
      b := round(b / 16);
      Form1.Canvas.Pixels[x,y+50] := RGB(r, g, b)
    end;
    Application.ProcessMessages;
  end;
end;



unit ProjetoX_Screen;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  ExtCtrls, StdCtrls, DBCtrls;

type
  TFormScreen = class(TForm)
    ImgFundo: TImage;
    procedure FormCreate(Sender: TObject);
  public
    MyRegion : HRGN;
    function BitmapToRegion(hBmp: TBitmap; TransColor: TColor): HRGN;
  end;

var
  FormScreen: TFormScreen;

implementation

{$R *.DFM}
function TFormScreen.BitmapToRegion(hBmp: TBitmap; TransColor: TColor): HRGN;

const
  ALLOC_UNIT = 100;

var
  MemDC, DC: HDC;
  BitmapInfo: TBitmapInfo;
  hbm32, holdBmp, holdMemBmp: HBitmap;
  pbits32 : Pointer;
  bm32 : BITMAP;
  maxRects: DWORD;
  hData: HGLOBAL;
  pData: PRgnData;
  b, CR, CG, CB : Byte;
  p32: pByte;
  x, x0, y: integer;
  p: pLongInt;
  pr: PRect;
  h: HRGN;

begin
  Result := 0;
  if hBmp <> nil then
  begin
    MemDC := CreateCompatibleDC(0);
    if MemDC <> 0 then
    begin
      with BitmapInfo.bmiHeader do
      begin
        biSize          := sizeof(TBitmapInfoHeader);
        biWidth         := hBmp.Width;
        biHeight        := hBmp.Height;
        biPlanes        := 1;
        biBitCount      := 32;
        biCompression   := BI_RGB;
        biSizeImage     := 0;
        biXPelsPerMeter := 0;
        biYPelsPerMeter := 0;
        biClrUsed       := 0;
        biClrImportant  := 0;
      end;
      hbm32 := CreateDIBSection(MemDC, BitmapInfo, DIB_RGB_COLORS, pbits32,0, 0);
      if hbm32 <> 0 then
      begin
        holdMemBmp := SelectObject(MemDC, hbm32);
        GetObject(hbm32, SizeOf(bm32), @bm32);
        while (bm32.bmWidthBytes mod 4) > 0 do
          inc(bm32.bmWidthBytes);
        DC := CreateCompatibleDC(MemDC);
        holdBmp := SelectObject(DC, hBmp.Handle);
        BitBlt(MemDC, 0, 0, hBmp.Width, hBmp.Height, DC, 0, 0, SRCCOPY);
        maxRects := ALLOC_UNIT;
        hData := GlobalAlloc(GMEM_MOVEABLE, sizeof(TRgnDataHeader) +
           SizeOf(TRect) * maxRects);
        pData := GlobalLock(hData);
        pData^.rdh.dwSize := SizeOf(TRgnDataHeader);
        pData^.rdh.iType := RDH_RECTANGLES;
        pData^.rdh.nCount := 0;
        pData^.rdh.nRgnSize := 0;
        SetRect(pData^.rdh.rcBound, MaxInt, MaxInt, 0, 0);
        CR := GetRValue(ColorToRGB(TransColor));
        CG := GetGValue(ColorToRGB(TransColor));
        CB := GetBValue(ColorToRGB(TransColor));
        p32 := bm32.bmBits;
        inc(PChar(p32), (bm32.bmHeight - 1) * bm32.bmWidthBytes);
        for y := 0 to hBmp.Height-1 do
        begin
          x := -1;
          while x+1 < hBmp.Width do
          begin
            inc(x);
            x0 := x;
            p := PLongInt(p32);
            inc(PChar(p), x * SizeOf(LongInt));
            while x < hBmp.Width do
            begin
              b := GetBValue(p^);
              if (b = CR) then
              begin
                b := GetGValue(p^);
                if (b = CG) then
                begin
                  b := GetRValue(p^);
                  if (b = CB) then
                    break;
                end;
              end;
              inc(PChar(p), SizeOf(LongInt));
              inc(x);
            end;
            if x > x0 then
            begin
              if pData^.rdh.nCount >= maxRects then
              begin
                GlobalUnlock(hData);
                inc(maxRects, ALLOC_UNIT);
                hData := GlobalReAlloc(hData, SizeOf(TRgnDataHeader) +
                   SizeOf(TRect) * maxRects, GMEM_MOVEABLE);
                pData := GlobalLock(hData);
                Assert(pData <> NIL);
              end;
              pr := @pData^.Buffer[pData^.rdh.nCount * SizeOf(TRect)];
              SetRect(pr^, x0, y, x, y+1);
              if x0 < pData^.rdh.rcBound.Left then
                pData^.rdh.rcBound.Left := x0;
              if y < pData^.rdh.rcBound.Top then
                pData^.rdh.rcBound.Top := y;
              if x > pData^.rdh.rcBound.Right then
                pData^.rdh.rcBound.Left := x;
              if y+1 > pData^.rdh.rcBound.Bottom then
                pData^.rdh.rcBound.Bottom := y+1;
              inc(pData^.rdh.nCount);
              if pData^.rdh.nCount = 2000 then
              begin
                h := ExtCreateRegion(NIL, SizeOf(TRgnDataHeader) +
                   (SizeOf(TRect) * maxRects), pData^);
                Assert(h <> 0);
                if Result <> 0 then
                begin
                  CombineRgn(Result, Result, h, RGN_OR);
                  DeleteObject(h);
                end else
                  Result := h;
                pData^.rdh.nCount := 0;
                SetRect(pData^.rdh.rcBound, MaxInt, MaxInt, 0, 0);
              end;
            end;
          end;
          Dec(PChar(p32), bm32.bmWidthBytes);
        end;
        h := ExtCreateRegion(NIL, SizeOf(TRgnDataHeader) +
           (SizeOf(TRect) * maxRects), pData^);
        Assert(h <> 0);
        if Result <> 0 then
        begin
          CombineRgn(Result, Result, h, RGN_OR);
          DeleteObject(h);
        end else
          Result := h;
        GlobalFree(hData);
        SelectObject(DC, holdBmp);
        DeleteDC(DC);
        DeleteObject(SelectObject(MemDC, holdMemBmp));
      end;
    end;
    DeleteDC(MemDC);
  end;
end;

procedure TFormScreen.FormCreate(Sender: TObject);
begin
        MyRegion := BitmapToRegion(imgFundo.Picture.Bitmap,imgFundo.Canvas.Pixels[0,0]);
        SetWindowRgn(Handle,MyRegion,True);
end;





procedure TFormXXXXXX.FormCreate(Sender: TObject);
begin
        FormScreen.MyRegion := FormScreen.BitmapToRegion(imgFundo.Picture.Bitmap,
          imgFundo.Canvas.Pixels[0,0]);
        SetWindowRgn(Handle,FormScreen.MyRegion,True);
end;


Взято из





Как активизировать предыдущий экземпляр вашей программы?


Как активизировать предыдущий экземпляр вашей программы?




Если внутренняя переменная hPrevInst не равна нулю, то она содержит дескриптор предыдущего запущенного экземпляра вашей программы. Вы просто находите открытое окно по его дескриптору и, при необходимости, выводите на передний план. Весь код расположен в файле .DPR file, НЕ в модуле. Строки, которые вам необходимо добавить к вашему .DPR-файлу, в приведенном ниже примере помечены {*}.


programOnce;

uses
{*}  WinTypes, WinProcs, SysUtils,

Forms,
Onceu in 'ONCEU.PAS' {Form1};

{$R *.RES}
{*}TYPE
{*}  PHWND = ^HWnd;

{*}  FUNCTION EnumWndProc(H : hWnd; P : PHWnd) : Integer; Export;
{*}  VAR ClassName : ARRAY[0..30] OF Char;
{*}  BEGIN
{*}    {Если это окно принадлежит предшествующему экземпляру...}
{*}    IF GetWindowWord(H, GWW_HINSTANCE) = hPrevInst THEN
{*}      BEGIN
{*}        {... проверяем КАКОЕ это окно.}
{*}        GetClassName(H, ClassName, 30);
{*}        {Если это главное окно приложения...}
{*}        IF StrIComp(ClassName, 'TApplication') = 0 THEN
{*}          BEGIN
{*}            {... ищем}
{*}{*}            P^ := H;
{*}            EnumWndProc := 0;
{*}          END;
{*}      END;
{*}  END;

{*}  PROCEDURE CheckPrevInst;
{*}  VAR PrevWnd : hWnd;
{*}  BEGIN
{*}    IF hPrevInst <> 0 THEN
{*}      {Предыдущий экземпляр запущен}
{*}      BEGIN
{*}        PrevWnd := 0;
{*}        EnumWindows(@EnumWndProc, LongInt(@PrevWnd));
{*}        {Ищем дескриптор окна предыдущего}
{*}        {экземпляра и активизируем его}
{*}        IF PrevWnd <> 0 THEN
{*}          IF IsIconic(PrevWnd) THEN
{*}            ShowWindow(PrevWnd, SW_SHOWNORMAL)
{*}          ELSE BringWindowToTop(PrevWnd);
{*}        Halt;
{*}      END;
{*}  END;
begin
{*}  CheckPrevInst;

Application.Title := 'Once';
Application.CreateForm(TForm1, Form1);
Application.Run;
end.



Взято с






Как автоматически помещать курсор мышки в центр контрола получившего фокус?


Как автоматически помещать курсор мышки в центр контрола получившего фокус?



Нам потребуется универсальная функция, которую можно будет применять для различных визуальных контролов.

Вот пример вызова нашей функции:

procedure TForm1.Button1Enter(Sender: TObject);
begin
  MoveMouseOverControl(Sender);
end;

Сама функция:

procedure MoveMouseOverControl(Sender: TObject);
var x,y: integer;
    point: TPoint;
begin
  with TControl(Sender) do
  begin
    x:= left + (width div 2);
    y:= top + (height div 2);
    point:= Parent.ClientToScreen(point);
    SetCursorPos(point.x, point.y);
  end;
end;

Взято с Исходников.ru



Как автоматически расширить TEdit?


Как автоматически расширить TEdit?



Следующий компонент автоматически подстраивается под текст, вводимый в него:

unit ExpandingEdit; 

interface 

uses 
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, 
  StdCtrls; 

type 
  TExpandingEdit = class(TEdit) 
  private 
    FCanvas: TControlCanvas; 
  protected 
    procedure Change; override; 
  public 
    constructor Create(AOwner: TComponent); override; 
    destructor Destroy; override; 
  end; 

procedure Register; 

implementation 

constructor TExpandingEdit.Create(AOwner: TComponent); 
begin 
  inherited Create(AOwner); 
  FCanvas := TControlCanvas.Create; 
  FCanvas.Control := Self; 
end; 

destructor TExpandingEdit.Destroy; 
begin 
  FCanvas.Free; 
  inherited Destroy; 
end; 

procedure TExpandingEdit.Change; 
const 
  EditMargin = 8; 
var 
  W: Integer; 
begin 
  inherited Change; 
  if not HandleAllocated then Exit; 
  FCanvas.Font := Font; 
  W := FCanvas.TextWidth(Text) + (2 * EditMargin); 
  if (Width < W) then Width := W; 
end; 

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

end.

Взято с Исходников.ru





Как автоматически заполнить поля формы в IE?


Как автоматически заполнить поля формы в IE?






  This example shows how to automatically fill in a search string 
  in the "Search Tip" page and click the search button. 


uses 
  MSHTML_TLB; 

// first navigate to tipspage 

procedure TForm1.Button1Click(Sender: TObject); 
begin 
  Webbrowser1.Navigate('http://www.swissdelphicenter.ch/en/tipsuchen.php'); 
end; 

// Try to access IE instance and fill out the search field with 
// a text and click the search button 

procedure TForm1.Button3Click(Sender: TObject); 
var 
  hIE: HWND; 
  ShellWindow: IShellWindows; 
  WB: IWebbrowser2; 
  spDisp: IDispatch; 
  IDoc1: IHTMLDocument2; 
  Document: Variant; 
  k, m: Integer; 
  ovElements: OleVariant; 
  i: Integer; 
begin 
  ShellWindow := CoShellWindows.Create; 
  // get the running instance of Internet Explorer 
  for k := 0 to ShellWindow.Count do 
  begin 
    spDisp := ShellWindow.Item(k); 
    if spDisp = nil then Continue; 
    // QueryInterface determines if an interface can be used with an object 
    spDisp.QueryInterface(iWebBrowser2, WB); 

    if WB <> nil then 
    begin 
      WB.Document.QueryInterface(IHTMLDocument2, iDoc1); 
      if iDoc1 <> nil then 
      begin 
        WB := ShellWindow.Item(k) as IWebbrowser2; 
        begin 
          Document := WB.Document; 

          // count forms on document and iterate through its forms 
          for m := 0 to Document.forms.Length - 1 do 
          begin 
            ovElements := Document.forms.Item(m).elements; 
            // iterate through elements 
            for i := 0 to ovElements.Length - 1 do 
            begin 
              // when input fieldname is found, try to fill out 
              try 
                if (CompareText(ovElements.item(i).tagName, 'INPUT') = 0) and 
                  (CompareText(ovElements.item(i).type, 'text') = 0) then 
                begin 
                  ovElements.item(i).Value := 'FindWindow'; 
                end; 
              except 
              end; 
              // when Submit button is found, try to click 
              try 
                if (CompareText(ovElements.item(i).tagName, 'INPUT') = 0) and 
                  (CompareText(ovElements.item(i).type, 'SUBMIT') = 0) and 
                  (ovElements.item(i).Value = 'Search') then  // Suchen fьr German 
                begin 
                  ovElements.item(i).Click; 
                end; 
              except 
              end; 
            end; 
          end; 
        end; 
      end; 
    end; 
  end; 
end; 

Взято с сайта



Как бы мне создать эдакий trackbar


Как бы мне создать эдакий trackbar в котором вместо широкой белой полоски с ползунком была бы тонкая линия?




В примере создается компонент, унаследованный от TTrackbar который переопределяет метод CreateParams и убират флаг TBS_ENABLESELRANGE из Style. Константа TBS_ENABLESELRANGE обьявленна в модуле CommCtrl.

uses CommCtrl, ComCtrls;

type
  TMyTrackBar = class(TTrackBar)
    procedure CreateParams(var Params: TCreateParams); override;
  end;

procedure TMyTrackBar.CreateParams(var Params: TCreateParams);
begin
  inherited;
  Params.Style := Params.Style and not TBS_ENABLESELRANGE;
end;

var
  MyTrackbar: TMyTrackbar;

procedure TForm1.Button1Click(Sender: TObject);
begin
  MyTrackBar := TMyTrackbar.Create(Form1);
  MyTrackbar.Parent := Form1;
  MyTrackbar.Left := 100;
  MyTrackbar.Top := 100;
  MyTrackbar.Width := 150;
  MyTrackbar.Height := 45;
  MyTrackBar.Visible := true;
end;



Как быстро нарисовать тень в заданном регионе?


Как быстро нарисовать тень в заданном регионе?




procedure TForm2.DrawShadows(WDepth, HDepth : Integer); 
var 
  Dst, RgnBox  : TRect; 
  hOldDC         : HDC; 
  OffScreen      : TBitmap; 
  Pattern          : TBitmap; 
  Bits               : array[0..7] of WORD; 
begin 
  Bits[0]:=$0055; 
  Bits[1]:=$00aa; 
  Bits[2]:=$0055; 
  Bits[3]:=$00aa; 
  Bits[4]:=$0055; 
  Bits[5]:=$00aa; 
  Bits[6]:=$0055; 
  Bits[7]:=$00aa; 
 
  hOldDC:=Canvas.Handle; 
  Canvas.Handle:=GetWindowDC(Form1.Handle); 
 
 
  OffsetRgn(ShadeRgn, WDepth, HDepth); 
  GetRgnBox(ShadeRgn, RgnBox); 
 
  Pattern:=TBitmap.Create; 
  Pattern.ReleaseHandle; 
  Pattern.Handle:=CreateBitmap(8, 8, 1, 1, @(Bits[0])); 
  Canvas.Brush.Bitmap:=Pattern; 
 
  OffScreen:=TBitmap.Create; 
  OffScreen.Width:=RgnBox.Right-RgnBox.Left; 
  OffScreen.Height:=RgnBox.Bottom-RgnBox.Top; 
  Dst:=Rect(0, 0, OffScreen.Width, OffScreen.Height); 
 
  OffsetRgn(ShadeRgn, 0, -RgnBox.Top); 
  FillRgn(OffScreen.Canvas.Handle, ShadeRgn, Canvas.Brush.Handle); 
 
  OffsetRgn(ShadeRgn, 0, RgnBox.Top); 
 
//  BitBlt работает быстрее CopyRect 
  BitBlt(OffScreen.Canvas.Handle, 0, 0, OffScreen.Width, OffScreen.Height, 
         Canvas.Handle, RgnBox.Left, RgnBox.Top, SRCAND); 
 
  Canvas.Brush.Color:=clBlack; 
  FillRgn(Canvas.Handle, ShadeRgn, Canvas.Brush.Handle); 
 
  BitBlt(Canvas.Handle, RgnBox.Left, RgnBox.Top, OffScreen.Width, 
   OffScreen.Height, OffScreen.Canvas.Handle, 0, 0, SRCPAINT); 
 
  OffScreen.Free; 
  Pattern.Free; 
 
  OffsetRgn(ShadeRgn, -WDepth, -HDepth); 
 
  ReleaseDC(Form1.Handle, Canvas.Handle); 
  Canvas.Handle:=hOldDC; 
end; 

Комментарии :
Функция рисует тень сложной формы на форме Form2.
Для определения формы тени используется регион ShadeRgn, который был создан где-то раньше (например в OnCreate). Относительно регионов см. Win32 API.

Титов Игорь Евгеньевич
infos@obninsk.ru




Как быстро выводить графику?


Как быстро выводить графику?




Как быстро выводить графику (a то Canvas очень медленно работает)

Вот пример заполнения формы точками случайного цвета:

type
TRGB = record
    b, g, r: byte;
  end;
  ARGB = array[0..1] of TRGB;
  PARGB = ^ARGB;

var
  b: TBitMap;

procedure TForm1.FormCreate(sender: TObject);
begin
  b := TBitMap.Create;
  b.pixelformat := pf24bit;
  b.width := Clientwidth;
  b.height := Clientheight;
end;

procedure TForm1.Tim1OnTimer(sender: TObject);
var
  p: PARGB;
  x, y: integer;
begin
  for y := 0 to b.height - 1 do
  begin
    p := b.scanline[y];
    for x := 0 to b.width - 1 do
    begin
      p[x].r := random(256);
      p[x].g := random(256);
      p[x].b := random(256);
    end;
  end;
  canvas.draw(0, 0, b);
end;

procedure TForm1.FormDestroy(sender: TObject);
begin
  b.free;
end;



Взято из





Как читать/писать в I/O порты?


Как читать/писать в I/O порты?



В Delphi 1 записывать и считывать из портов можно через глобальный массив 'ports'. Однако данная возможность отсутствует в '32-битном' Delphi.

Следующие две функции можно использовать в любой версии delphi:


function InPort(PortAddr:word): byte; 
{$IFDEF WIN32} 
assembler; stdcall; 
asm 
        mov dx,PortAddr 
        in al,dx 
end; 
{$ELSE} 
begin 
  Result := Port[PortAddr]; 
end; 
{$ENDIF} 

procedure OutPort(PortAddr:   
          word; Databyte: byte); 
{$IFDEF WIN32} 
assembler; stdcall; 
asm 
   mov al,Databyte 
   mov dx,PortAddr 
   out dx,al 
end; 
{$ELSE} 
begin 
  Port[PortAddr] := DataByte; 
end; 
{$ENDIF} 

Взято с Исходников.ru





Как динамически прочитать информацию о классе


Как динамически прочитать информацию о классе





procedureTForm1.FormCreate(Sender: TObject);
begin
  {This only works for classes registered using RegisterClass}
  RegisterClasses([TButton, TForm]);
end;


procedure TForm1.Button1Click(Sender: TObject);
var
  CRef: TPersistentClass;
  PTI: PTypeInfo;
  AControl: TControl;
begin
  CRef := GetClass('TButton');
  if CRef <> nil then
  begin
    AControl := TControl(TControlClass(CRef).Create(Self));
    with AControl do
    begin
      Parent := Self;
      Width := 50;
      Height := 30;
    end;
    Inc(Id);
  end
  else
    MessageDlg('No such class', mtWarning, [mbOk], 0);
end;


Взято из






Как добавить Cookies?


Как добавить Cookies?



Пример демонстрирует создание cookie посредствам стандартного компонента Delphi

procedure TwebDispatcher.WebAction(Sender: TObject; Request: TWebRequest; 
  Response: TWebResponse; var Handled: Boolean); 
begin 
    with (Response.Cookies.Add) do begin 
      Name := 'TESTNAME'; 
      Value := 'TESTVALUE'; 
      Secure := False; 
      Expires := Now; 
      Response.Cookies.WebResponse.SendResponse; 
    end; 
end; 


Взято с Исходников.ru



Как добавить документ в меню Пуск -> Документы?


Как добавить документ в меню Пуск -> Документы?



Используйте функцию SHAddToRecentDocs.

uses ShlOBJ;

procedure TForm1.Button1Click(Sender: TObject);
var
   s : string;
begin
   s := 'C:\DownLoad\ntkfaq.html';
   SHAddToRecentDocs(SHARD_PATH, pChar(s));
end;


Взято с Исходников.ru






Как добавить горизонтальную полосу прокрутки (scrollbar) в TListBox?


Как добавить горизонтальную полосу прокрутки (scrollbar) в TListBox?



В Delphi компонент TListBox автоматически включает в себя вертикальный scrollbar. Полоска прокрутки появляется в том случае, если все элементы списка не помещаются в видимую область списка. Однако, list box не показывает горизонтального скролбара, когда ширина элементов превышает ширину списка. Конечно же существует способ добавить горизонтальную полосу прокрутки.

Добавьте следующий код в событие Вашей формы OnCreate.

procedure TForm1.FormCreate(Sender: TObject); 
var 
  i, MaxWidth: integer; 
begin 
  MaxWidth := 0; 
  for i := 0 to LB1.Items.Count - 1 do 
  if MaxWidth < LB1.Canvas.TextWidth(LB1.Items.Strings[i]) then 
    MaxWidth := LB1.Canvas.TextWidth(LB1.Items.Strings[i]); 
  SendMessage(LB1.Handle, LB_SETHORIZONTALEXTENT, MaxWidth+2, 0); 
end; 

Приведённый код определяет ширину в пикселях самой длинной строки списка. Затем он использует сообщение LB_SETHORIZONTALEXTENT, чтобы установить ширину горизонтального скролбара в пикселях. Два дополнительных пикселя добавленные к MaxWidth служат для стрелки в правом углу list box-а.

Взято с Исходников.ru






Как добавить кнопку?


Как добавить кнопку?





type
TConnType = (COM_OBJECT, EXPLORER_BAR, SCRIPT, EXECUTABLE);

function AddBandToolbarBtn(Visible: Boolean; ConnType: TConnType;
  BtnText, HotIcon, Icon, GuidOrPath: string): string;
var
  GUID: TGUID;
  Reg: TRegistry;
  ID: string;
begin
  CreateGuid(GUID);
  ID := GuidToString(GUID);
  Reg := TRegistry.Create;
  with Reg do
  try
    RootKey := HKEY_LOCAL_MACHINE;
    OpenKey('\Software\Microsoft\Internet Explorer\Extensions\'
      + ID, True);
    if Visible then
      WriteString('Default Visible', 'Yes')
    else
      WriteString('Default Visible', 'No');
    WriteString('ButtonText', BtnText);
    WriteString('HotIcon', HotIcon);
    WriteString('Icon', Icon);
    case ConnType of
      COM_OBJECT:
        begin
          WriteString('CLSID', '{1FBA04EE-3024-11d2-8F1F-0000F87ABD16}');
          WriteString('ClsidExtension', GuidOrPath);
        end;
      EXPLORER_BAR:
        begin
          WriteString('CLSID', '{E0DD6CAB-2D10-11D2-8F1A-0000F87ABD16}');
          WriteString('BandCLSID', GuidOrPath);
        end;
      EXECUTABLE:
        begin
          WriteString('CLSID', '{1FBA04EE-3024-11D2-8F1F-0000F87ABD16}');
          WriteString('Exec', GuidOrPath);
        end;
      SCRIPT:
        begin
          writeString('CLSID', '{1FBA04EE-3024-11D2-8F1F-0000F87ABD16}');
          WriteString('Script', GuidOrPath);
        end;
    end;
    CloseKey;
    OpenKey('\Software\IE5Tools\ToolBar Buttons\', True);
    WriteString(BtnText, ID);
    CloseKey;
  finally
    Free;
  end;
  Result := ID;
end;


Взято с

Delphi Knowledge Base






Как добавить кнопку в панель инструментов IE?


Как добавить кнопку в панель инструментов IE?



1. ButtonText = Всплывающая подсказка к кнопке
2. MenuText = Текст, который будет использован для пункта в меню "Сервис"
3. MenuStatusbar = *Ignore*
4. CLSID = Ваш уникальный classID. Для создания нового CLSID (для каждой кнопки) можно использовать GUIDTOSTRING.
5. Default Visible := Показать ей.
6. Exec := Путь к Вашей программе.
7. Hoticon := иконка из shell32.dll когда мышка находится над кнопкой
8. Icon := иконка из shell32.dll

procedure CreateExplorerButton;
const
  TagID = '\{10954C80-4F0F-11d3-B17C-00C0DFE39736}\';
var
  Reg: TRegistry;
   ProgramPath: string;
  RegKeyPath: string;
begin
 ProgramPath := 'c:\folder\exename.exe';
 Reg := TRegistry.Create;
 try
  with Reg do begin
   RootKey := HKEY_LOCAL_MACHINE;
   RegKeyPath := 'Software\Microsoft\Internet Explorer\Extensions';
   OpenKey(RegKeyPath + TagID, True);
   WriteString('ButtonText', 'Your program Button text');
   WriteString('MenuText', 'Your program Menu text');
   WriteString('MenuStatusBar', 'Run Script');
   WriteString('ClSid', '{1FBA04EE-3024-11d2-8F1F-0000F87ABD16}');
   WriteString('Default Visible', 'Yes'); 
   WriteString('Exec', ProgramPath);
   WriteString('HotIcon', ',4');
   WriteString('Icon', ',4');
  end
 finally
  Reg.CloseKey;
  Reg.Free;
 end;
end;

После выполнения этого кода достаточно просто запустить IE.

Взято с Исходников.ru




Как добавить когерентный шум?


Как добавить когерентный шум?





{Coherentnoise function over 1, 2 or 3 dimensions by Ken Perlin}

unit perlin;

interface

function noise1(arg: double): double;
function noise2(vec0, vec1: double): double;
function noise3(vec0, vec1, vec2: double): double;
function PNoise1(x, alpha, beta: double; n: integer): double;
function PNoise2(x, y, alpha, beta: double; n: integer): double;
function PNoise3(x, y, z, alpha, beta: double; n: integer): double;

{High Alpha: smoother intensity change, lower contrast
Low Alpha: rapid intensity change, higher contrast
High Beta: coarse, big spots
Low Beta: fine, small spots}

implementation

uses
  SysUtils;

const
  defB = $100;
  defBM = $FF;
  defN = $1000;

var
  start: boolean = true;
  p: array[0..defB + defB + 2 - 1] of integer;
  g3: array[0..defB + defB + 2 - 1, 0..2] of double;
  g2: array[0..defB + defB + 2 - 1, 0..1] of double;
  g1: array[0..defB + defB + 2 - 1] of double;

function s_curve(t: double): double;
begin
  result := t * t * (3.0 - 2.0 * t);
end;

function lerp(t, a, b: double): double;
begin
  result := a + t * (b - a);
end;

procedure setup(veci: double; var b0, b1: integer; var r0, r1: double);
var
  t: double;
begin
  t := veci + defN;
  b0 := trunc(t) and defBM;
  b1 := (b0 + 1) and defBM;
  r0 := t - int(t);
  r1 := r0 - 1.0;
end;

procedure normalize2(var v0, v1: double);
var
  s: double;
begin
  s := sqrt(v0 * v0 + v1 * v1);
  v0 := v0 / s;
  v1 := v1 / s;
end;

procedure normalize3(var v0, v1, v2: double);
var
  s: double;
begin
  s := sqrt(v0 * v0 + v1 * v1 + v2 * v2);
  v0 := v0 / s;
  v1 := v1 / s;
  v2 := v2 / s;
end;

procedure init;
var
  i, j, k: integer;
begin
  for i := 0 to defB - 1 do
  begin
    p[i] := i;
    g1[i] := (random(defB + defB) - defB) / defB;
    for j := 0 to 1 do
      g2[i, j] := (random(defB + defB) - defB) / defB;
    normalize2(g2[i, 0], g2[i, 1]);
    for j := 0 to 2 do
      g3[i, j] := (random(defB + defB) - defB) / defB;
    normalize3(g3[i, 0], g3[i, 1], g3[i, 2]);
  end;
  i := defB;
  while i > 0 do
  begin
    k := p[i];
    j := random(defB);
    p[i] := p[j];
    p[j] := k;
    dec(i);
  end;
  for i := 0 to defB + 1 do
  begin
    p[defB + i] := p[i];
    g1[defB + i] := g1[i];
    for j := 0 to 1 do
      g2[defB + i, j] := g2[i, j];
    for j := 0 to 2 do
      g3[defB + i, j] := g3[i, j];
  end;
end;

function noise1(arg: double): double;
var
  bx0, bx1: integer;
  rx0, rx1, sx, u, v: double;
begin
  if start then
  begin
    init;
    start := false;
  end;
  bx0 := trunc(arg + defN) and defBM;
  bx1 := (bx0 + 1) and defBM;
  rx0 := frac(arg + defN);
  rx1 := rx0 - 1.0;
  sx := rx0 * rx0 * (3.0 - 2.0 * rx0);
  u := rx0 * g1[p[bx0]];
  v := rx1 * g1[p[bx1]];
  result := u + sx * (v - u);
end;

function noise2(vec0, vec1: double): double;
var
  i, j, bx0, bx1, by0, by1, b00, b10, b01, b11: integer;
  rx0, rx1, ry0, ry1, sx, sy, a, b, u, v: double;
begin
  if start then
  begin
    init;
    start := false;
  end;
  bx0 := trunc(vec0 + defN) and defBM;
  bx1 := (bx0 + 1) and defBM;
  rx0 := frac(vec0 + defN);
  rx1 := rx0 - 1.0;
  by0 := trunc(vec1 + defN) and defBM;
  by1 := (by0 + 1) and defBM;
  ry0 := frac(vec1 + defN);
  ry1 := ry0 - 1.0;
  i := p[bx0];
  j := p[bx1];
  b00 := p[i + by0];
  b10 := p[j + by0];
  b01 := p[i + by1];
  b11 := p[j + by1];
  sx := rx0 * rx0 * (3.0 - 2.0 * rx0);
  sy := ry0 * ry0 * (3.0 - 2.0 * ry0);
  u := rx0 * g2[b00, 0] + ry0 * g2[b00, 1];
  v := rx1 * g2[b10, 0] + ry0 * g2[b10, 1];
  a := u + sx * (v - u);
  u := rx0 * g2[b01, 0] + ry1 * g2[b01, 1];
  v := rx1 * g2[b11, 0] + ry1 * g2[b11, 1];
  b := u + sx * (v - u);
  result := a + sy * (b - a);
end;

function noise3orig(vec0, vec1, vec2: double): double;
var
  i, j, bx0, bx1, by0, by1, bz0, bz1, b00, b10, b01, b11: integer;
  rx0, rx1, ry0, ry1, rz0, rz1, sx, sy, sz, a, b, c, d, u, v: double;
begin
  if start then
  begin
    start := false;
    init;
  end;
  setup(vec0, bx0, bx1, rx0, rx1);
  setup(vec1, by0, by1, ry0, ry1);
  setup(vec2, bz0, bz1, rz0, rz1);
  i := p[bx0];
  j := p[bx1];
  b00 := p[i + by0];
  b10 := p[j + by0];
  b01 := p[i + by1];
  b11 := p[j + by1];
  sx := s_curve(rx0);
  sy := s_curve(ry0);
  sz := s_curve(rz0);
  u := rx0 * g3[b00 + bz0, 0] + ry0 * g3[b00 + bz0, 1] + rz0 * g3[b00 + bz0, 2];
  v := rx1 * g3[b10 + bz0, 0] + ry0 * g3[b10 + bz0, 1] + rz0 * g3[b10 + bz0, 2];
  a := lerp(sx, u, v);
  u := rx0 * g3[b01 + bz0, 0] + ry1 * g3[b01 + bz0, 1] + rz0 * g3[b01 + bz0, 2];
  v := rx1 * g3[b11 + bz0, 0] + ry1 * g3[b11 + bz0, 1] + rz0 * g3[b11 + bz0, 2];
  b := lerp(sx, u, v);
  c := lerp(sy, a, b);
  u := rx0 * g3[b00 + bz1, 0] + ry0 * g3[b00 + bz1, 1] + rz1 * g3[b00 + bz1, 2];
  v := rx1 * g3[b10 + bz1, 0] + ry0 * g3[b10 + bz1, 1] + rz1 * g3[b10 + bz1, 2];
  a := lerp(sx, u, v);
  u := rx0 * g3[b01 + bz1, 0] + ry1 * g3[b01 + bz1, 1] + rz1 * g3[b01 + bz1, 2];
  v := rx1 * g3[b11 + bz1, 0] + ry1 * g3[b11 + bz1, 1] + rz1 * g3[b11 + bz1, 2];
  b := lerp(sx, u, v);
  d := lerp(sy, a, b);
  result := lerp(sz, c, d);
end;

function noise3(vec0, vec1, vec2: double): double;
var
  i, j, bx0, bx1, by0, by1, bz0, bz1, b00, b10, b01, b11: integer;
  rx0, rx1, ry0, ry1, rz0, rz1, sx, sy, sz, a, b, c, d, u, v: double;
begin
  if start then
  begin
    start := false;
    init;
  end;
  bx0 := trunc(vec0 + defN) and defBM;
  bx1 := (bx0 + 1) and defBM;
  rx0 := frac(vec0 + defN);
  rx1 := rx0 - 1.0;
  by0 := trunc(vec1 + defN) and defBM;
  by1 := (by0 + 1) and defBM;
  ry0 := frac(vec1 + defN);
  ry1 := ry0 - 1.0;
  bz0 := trunc(vec2 + defN) and defBM;
  bz1 := (bz0 + 1) and defBM;
  rz0 := frac(vec2 + defN);
  rz1 := rz0 - 1.0;
  i := p[bx0];
  j := p[bx1];
  b00 := p[i + by0];
  b10 := p[j + by0];
  b01 := p[i + by1];
  b11 := p[j + by1];
  sx := rx0 * rx0 * (3.0 - 2.0 * rx0);
  sy := ry0 * ry0 * (3.0 - 2.0 * ry0);
  sz := rz0 * rz0 * (3.0 - 2.0 * rz0);
  u := rx0 * g3[b00 + bz0, 0] + ry0 * g3[b00 + bz0, 1] + rz0 * g3[b00 + bz0, 2];
  v := rx1 * g3[b10 + bz0, 0] + ry0 * g3[b10 + bz0, 1] + rz0 * g3[b10 + bz0, 2];
  a := u + sx * (v - u);
  u := rx0 * g3[b01 + bz0, 0] + ry1 * g3[b01 + bz0, 1] + rz0 * g3[b01 + bz0, 2];
  v := rx1 * g3[b11 + bz0, 0] + ry1 * g3[b11 + bz0, 1] + rz0 * g3[b11 + bz0, 2];
  b := u + sx * (v - u);
  c := a + sy * (b - a);
  u := rx0 * g3[b00 + bz1, 0] + ry0 * g3[b00 + bz1, 1] + rz1 * g3[b00 + bz1, 2];
  v := rx1 * g3[b10 + bz1, 0] + ry0 * g3[b10 + bz1, 1] + rz1 * g3[b10 + bz1, 2];
  a := u + sx * (v - u);
  u := rx0 * g3[b01 + bz1, 0] + ry1 * g3[b01 + bz1, 1] + rz1 * g3[b01 + bz1, 2];
  v := rx1 * g3[b11 + bz1, 0] + ry1 * g3[b11 + bz1, 1] + rz1 * g3[b11 + bz1, 2];
  b := u + sx * (v - u);
  d := a + sy * (b - a);
  result := c + sz * (d - c);
end;

{Harmonic summing functions}

{In what follows "alpha" is the weight when the sum is formed. Typically it is 2. As this
approaches 1 the function is noisier.
"beta" is the harmonic scaling/spacing, typically 2.
persistance = 1/alpha
beta = frequency
N = octaves}

function PNoise1(x, alpha, beta: double; n: integer): double;
var
  i: integer;
  val, sum, p, scale: double;
begin
  sum := 0;
  scale := 1;
  p := x;
  for i := 0 to n - 1 do
  begin
    val := noise1(p);
    sum := sum + val / scale;
    scale := scale * alpha;
    p := p * beta;
  end;
  result := sum;
end;

function PNoise2(x, y, alpha, beta: double; n: integer): double;
var
  i: integer;
  val, sum, px, py, scale: double;
begin
  sum := 0;
  scale := 1;
  px := x;
  py := y;
  for i := 0 to n - 1 do
  begin
    val := noise2(px, py);
    sum := sum + val / scale;
    scale := scale * alpha;
    px := px * beta;
    py := py * beta;
  end;
  result := sum;
end;

function PNoise3(x, y, z, alpha, beta: double; n: integer): double;
var
  i: integer;
  val, sum, px, py, pz, scale: double;
begin
  sum := 0;
  scale := 1;
  px := x;
  py := y;
  pz := z;
  for i := 0 to n - 1 do
  begin
    val := noise3(px, py, pz);
    sum := sum + val / scale;
    scale := scale * alpha;
    px := px * beta;
    py := py * beta;
    pz := pz * beta;
  end;
  result := sum;
end;

end.



Used like this:


unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, ExtCtrls, StdCtrls;

type
  TForm1 = class(TForm)
    Image1: TImage;
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

uses
  perlin;

procedure TForm1.Button1Click(Sender: TObject);
var
  x, y, z, c: integer;
begin
  image1.Canvas.Brush.Color := 0;
  image1.Canvas.FillRect(image1.Canvas.ClipRect);
  for x := 0 to 511 do
    for y := 0 to 511 do
    begin
      z := trunc(pnoise2(x / 100, y / 100, 2, 2, 10) * 128) + 128;
      c := z + (z shl 8) + (z shl 16);
      image1.Canvas.Pixels[x, y] := c;
    end;
  c := 0;
  repeat
    image1.Canvas.Pixels[519, c] := $FFFFFF;
    c := c + 10;
  until
    c > 510;
end;

end.

Взято с

Delphi Knowledge Base






Как добавить копию текущей записи?


Как добавить копию текущей записи?



Следующая функция добавит в конец данных точную копию текущей записи.

procedure AppendCurrent(Dataset:Tdataset); 
var 
  aField : Variant ; 
  i      : Integer ; 
begin 
  // Создаём массив
  aField := VarArrayCreate([0,DataSet.Fieldcount-1],VarVariant); 

  // считываем значения в массив
  for i := 0 to (DataSet.Fieldcount-1) do 
     aField[i] := DataSet.fields[i].Value ; 

  DataSet.Append ; 

  // помещаем значения массива в новую запись
  for i := 0 to (DataSet.Fieldcount-1) do 
     DataSet.fields[i].Value := aField[i] ; 
end;

Взято с Исходников.ru

Примечания Vit:
1) Если таблица имеет ключевые поля или уникальные индексы данный код приведёт к ошибке "Key violation"





Как добавить нужный язык в систему?


Как добавить нужный язык в систему?



Автор: Mekan Gara

Для этого необходимо изменить некоторые ключи в реестре. Например, необходимо добавить Туркменский язык. Конечно, Вам необходимо иметь файл KBD с раскладкой клавиатуры (Turkmen.kbd).

procedure TTMKBD.OkClick(Sender: TObject); 
var reg:TRegistry; 
     srs,dst:string; 
begin 
   Reg := TRegistry.Create; 
  with Reg do 
    try 
      RootKey :=HKEY_LOCAL_MACHINE; 
      OpenKey('\System\CurrentControlSet\Control\keyboard layouts\00000405', True); 
      WriteString('layout file','Turkmen.kbd'); 
      WriteString('layout text','Turkmen'); 
      OpenKey('\System\CurrentControlSet\Control\Nls\Locale', True); 
      WriteString('00000405','Turkmen'); 
      CloseKey; 
    finally 
      Free; 
    end; 
  srs:='Turkmen.kbd'; 
  dst:='c:\windows\system\Turkmen.kbd'; 
Filecopy(srs,dst); 
showmessage('Well Done it all'); 
close; 
end;

Взято с Исходников.ru





Как добавить пункт в контекстное меню IE?


Как добавить пункт в контекстное меню IE?



В реестре по адресу:

HKEY_CURRENT_USER
SOFTWARE
Microsoft
Internet Explorer
MenuExt


два параметра, посмотришь на другие-поймешь что они означают.

Автор ответа: Mikel
Взято с Vingrad.ru




Как добавить собственную панель?


Как добавить собственную панель?





functionAddExplorerBar(BarTitle, Url: string; BarSize: Int64; Horizontal:
  Boolean): string;
const
  EXPLORERBAR_ID = '{4D5C8C2A-D075-11d0-B416-00C04FB90376}';
  VERTICAL_BAR = '{00021493-0000-0000-C000-000000000046}';
  HORIZONTAL_BAR = '{00021494-0000-0000-C000-000000000046}';
var
  GUID: TGUID;
  SysDir, ID: string;
  Reg: TRegistry;
begin
  CreateGuid(GUID);
  ID := GuidToString(GUID);
  Reg := TRegistry.Create;
  with Reg do
  try
    RootKey := HKEY_CLASSES_ROOT;
    OpenKey('\CLSID\' + ID, True);
    WriteString('', 'BarTitle');
    CloseKey;
    CreateKey('\CLSID\' + ID + '\Implemented Categories');
    if HORIZONTAL then
      CreateKey('\CLSID\' + ID + '\Implemented Categories\' +
        HORIZONTAL_BAR)
    else
      CreateKey('\CLSID\' + ID + '\Implemented Categories\' +
        VERTICAL_BAR);
    SetLength(SysDir, 255);
    GetSysDirectory(PChar(SysDir), 255);
    SysDir := PChar(SysDir) + '\SHDOCVW.DLL';
    OpenKey('\CLSID\' + ID + '\InProcServer32', True);
    Writestring('', SysDir);
    WriteString('Threadingmodel', 'Apartment');
    CloseKey;
    OpenKey('\CLSID\' + ID + '\Instance', True);
    WriteString('CLSID', EXPLORERBAR_ID);
    CloseKey;
    OpenKey('\CLSID\' + ID + '\Instance\InitPropertyBag', True);
    WriteString('Url', URL);
    CloseKey;
    RootKey := HKEY_LOCAL_MACHINE;
    OpenKey('Software\Microsoft\Internet Explorer\Explorer Bars\'
      + ID, True);
    WriteBinaryData('BarSize', BarSize, SizeOf(BarSize));
    CloseKey;
    OpenKey('\Software\IE5Tools\Explorer Bars\', True);
    WriteString(BarTitle, ID);
    CloseKey;
    OpenKey('\Software\Microsoft\Internet Explorer\Toolbar', True)
      WriteString(ID, '');
    CloseKey;
  finally
    Free;
  end;
  result := ID;
end;

Взято с

Delphi Knowledge Base






Как добавить событие OnMouseLeave?


Как добавить событие OnMouseLeave?




Все потомки TComponent могут посылать сообщения CM_MOUSEENTER и CM_MOUSELEAVE во время вхождения и покидания курсора мыши области компонента. Если вам необходимо, чтобы ваши компоненты обладали реакцией на эти события, необходио написать для них соответствующие обработчики.

procedureCMMouseEnter(var msg:TMessage); message CM_MOUSEENTER;
procedure CMMouseLeave(var msg: TMessage); message CM_MOUSELEAVE;
..
..
..
procedure MyComponent.CMMouseEnter(var msg:TMessage);
begin

inherited;
{действия на вход мыши в область компонента}
end;

procedure MyComponent.CMMouseLeave(var msg: TMessage);
begin

inherited;
{действия на покидание мыши области компонента}
end; 

Дополнение

Часто приходится сталкиваться с ситуацией, когда необходимо обработать два важных события для визуальных компонентов:

MouseEnter - когда событие мыши входит в пределы визуального компонента;
MouseLeave - когда событие мыши оставляет его пределы.
Известно, что все Delphi объявляет эти сообщения в виде:
Cm_MouseEnter;
Cm_MouseLeave.
Т.е. все визуальные компоненты, которые порождены от TControl, могут отлавливать эти события. Следующий пример показывает как создать наследника от TLabel и добавить два необходимых события OnMouseLeave и OnMouseEnter.



(*///////////////////////////////////////////////////////*)
(*// Author: Briculski Serge
(*// E-Mail: bserge@airport.md
(*// Date: 26 Apr 2000
(*///////////////////////////////////////////////////////*)

unit BS_Label;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls;

type
  TBS_Label = class(TLabel)
  private
    { Private declarations }
    FOnMouseLeave: TNotifyEvent;
    FOnMouseEnter: TNotifyEvent;
    procedure CMMouseEnter(var Message: TMessage); message CM_MOUSEENTER;
    procedure CMMouseLeave(var Message: TMessage); message CM_MOUSELEAVE;
  protected
    { Protected declarations }
  public
    { Public declarations }
  published
    { Published declarations }
    property OnMouseLeave: TNotifyEvent read FOnMouseLeave write FOnMouseLeave;
    property OnMouseEnter: TNotifyEvent read FOnMouseEnter write FOnMouseEnter;
  end;

procedure Register;

implementation

procedure Register;
begin
  RegisterComponents('Custom', [TBS_Label]);
end;

{ TBS_Label }

procedure TBS_Label.CMMouseEnter(var Message: TMessage);
begin
  if Assigned(FOnMouseEnter) then
    FOnMouseEnter(Self);
end;

procedure TBS_Label.CMMouseLeave(var Message: TMessage);
begin
  if Assigned(FOnMouseLeave) then
    FOnMouseLeave(Self);
end;

end.

Взято с






Как добавить свой пункт меню?


Как добавить свой пункт меню?





functionAddMenuItem(ConnType: TconnType; MenuText, StatusBarText,
  GuidOrPath: string; HelpMenu: Boolean): string;
var
  GUID: TGUID;
  ID: string;
  Reg: TRegistry;
begin
  CreateGuid(GUID);
  ID := GuidToString(GUID);
  Reg := TRegistry.Create;
  with Reg do
  begin
    RootKey := HKEY_LOCAL_MACHINE;
    OpenKey('\Software\Microsoft\Internet Explorer\Extensions\'
      + ID, True);
    if HelpMenu then
      WriteString('MenuCostumize', 'help');
    WriteString('CLSID', '{1FBA04EE-3024-11d2-8F1F-0000F87ABD16}');
    WriteString('MenuText', MenuText);
    WriteString('MenuStatusBar', StatusBarText);
    case ConnType of
      EXECUTABLE: WriteString('Exec', GuidOrPath);
      COM_OBJECT: WriteString('ClsidExtension', GuidOrPath);
      SCRIPT: WriteString('Script', GuidOrPath);
    end;
    CloseKey;
    OpenKey('\Software\IE5Tools\Menu Items\', True);
    WriteString(MenuText, ID);
    CloseKey;
    Free;
  end;
  Result := ID;
end;


Взято с

Delphi Knowledge Base






Как добавить TCheckBox в TStringGrid?


Как добавить TCheckBox в TStringGrid?



Компилятор: Delphi

Автор: Joel E. Cant.

Пример демонстрирует добавление любого количества чекбоксов в StringGrid.
В этом примере необходимо добавить TPanel, а в саму панель включить TstringGrid.
Так же необходимо добавить невидимый TcheckBox на форму. Затем добавьте
5 колонок и 4 строки в объект StringGrid.

procedure TForm1.CheckBox1Click(Sender: TObject);
begin
ShowMessage('There it is!!');  
end;

// Заполняем заголовок StringGrid
procedure TForm1.FormCreate(Sender: TObject);
begin
StringGrid1.Cells[0,0] := 'A Simple';  
StringGrid1.Cells[1,0] := 'Way';  
StringGrid1.Cells[2,0] := 'To';  
StringGrid1.Cells[3,0] := 'Do It';  
StringGrid1.Cells[4,0] := 'Check !!';  
AddCheckBoxes; // добавляем чекбоксы...  
end;

procedure TForm1.AddCheckBoxes;
var
i: Integer;  
NewCheckBox: TCheckBox;  
begin
clean_previus_buffer; // очищаем неиспользуемые чекбоксы...  
for i := 1 to 4 do  
begin  
StringGrid1.Cells[0,i] := 'a';  
StringGrid1.Cells[1,i] := 'b';  
StringGrid1.Cells[2,i] := 'c';  
StringGrid1.Cells[3,i] := 'd';  
NewCheckBox := TCheckBox.Create(Application);  
NewCheckBox.Width := 0;  
NewCheckBox.Visible := false;  
NewCheckBox.Caption := 'OK';  
NewCheckBox.Color := clWindow;  
NewCheckBox.Tag := i;  
NewCheckBox.OnClick := CheckBox1.OnClick; //Связываем предыдущее событие OnClick  
                                          // с существующим TCheckBox  
NewCheckBox.Parent := Panel1;  
StringGrid1.Objects[4,i] := NewCheckBox;  
StringGrid1.RowCount := i;  
end;  
set_checkbox_alignment; // расположение чекбоксов в ячейках таблицы...  
end;

Procedure TForm1.clean_previus_buffer;
var
NewCheckBox: TCheckBox;  
i: Integer;  
begin
for i := 1 to StringGrid1.RowCount do  
begin  
NewCheckBox := (StringGrid1.Objects[4,i] as TCheckBox);  
if NewCheckBox <> nil then  
begin  
NewCheckBox.Visible := false;  
StringGrid1.Objects[4,i] := nil;  
end;  
end;  
end;

Procedure TForm1.set_checkbox_alignment;
var
NewCheckBox: TCheckBox;  
Rect: TRect;  
i: Integer;  
begin
for i := 1 to StringGrid1.RowCount do  
begin  
NewCheckBox := (StringGrid1.Objects[4,i] as TCheckBox);  
if NewCheckBox <> nil then  
begin  
Rect := StringGrid1.CellRect(4,i); // получаем размер ячейки для чекбокса  
NewCheckBox.Left := StringGrid1.Left + Rect.Left+2;  
NewCheckBox.Top := StringGrid1.Top + Rect.Top+2;  
NewCheckBox.Width := Rect.Right - Rect.Left;  
NewCheckBox.Height := Rect.Bottom - Rect.Top;  
NewCheckBox.Visible := True;  
end;  
end;  
end;

procedure TForm1.StringGrid1DrawCell(Sender: TObject; ACol, ARow: Integer;
Rect: TRect; State: TGridDrawState);
begin
if not (gdFixed in State) then set_checkbox_alignment;  
end;

Взято с Исходников.ru



Как добавить текст к закладке?


Как добавить текст к закладке?





uses
ComObj;

procedure TForm1.Button1Click(Sender: TObject);
const
  // Word Document to open
  YourWordDocument = 'c:\test\worddoc.doc';
var
  BookmarkName, Doc, R: OleVariant;
begin
  // Start a Word instance
  try
    WordApp := CreateOleObject('Word.Application');
  except
    ShowMessage('Could not start MS Word!');
  end;
  // Open your Word document
  WordApp.Documents.Open(YourWordDocument);
  Doc := WordApp.ActiveDocument;

  // name of your bookmark
  BookmarkName := 'MyBookMark';

  // Check if bookmark exists
  if Doc.Bookmarks.Exists(BookmarkName) then
  begin
    R := Doc.Bookmarks.Item(BookmarkName).Range;
    // Add text at our bookmark
    R.InsertAfter('Text in bookmark');
    // You make a text formatting like changing its color
    R.Font.Color := clRed;
  end;

  // Save your document and quit Word
  if not VarIsEmpty(WordApp) then
  begin
    WordApp.DisplayAlerts := 0;
    WordApp.Documents.Item(1).Save;
    WordApp.Quit;
    BookmarkName := Unassigned;
    R := Unassigned;
    WordApp := Unassigned;
  end;
end;


Взято с

Delphi Knowledge Base






Как добавить текст в footer документа?


Как добавить текст в footer документа?





Footer:



{... }
aDoc := WordApp.Documents.Add(EmptyParam, EmptyParam);
aDoc.Sections.Item(1).Footers.Item(wdHeaderFooterPrimary).Range.Text :=
  'This is a footer';
{ ... }


Взято с

Delphi Knowledge Base






Как добавить текст в header документа?


Как добавить текст в header документа?





{... }
aDoc := WordApp.Documents.Add(EmptyParam, EmptyParam);
aDoc.Sections.Item(1).Headers.Item(wdHeaderFooterPrimary).Range.Text :=
  'This is a header';
{ ... }


Взято с

Delphi Knowledge Base






Как добавить текущую страницу TWebbrowser в favorites?


Как добавить текущую страницу TWebbrowser в favorites?





// You need: 1 TEdit, 2 TButtons, 1 TWebbrowser 
// Du brauchst: 1 TEdit, 2 TButtons, 1 TWebbrowser 

const 
  NotAllowed: set of Char = ['"'] + ['/'] + ['\'] + ['?'] + [':'] + ['*'] + 
    ['<'] + ['>'] + ['|']; 

implementation 

{$R *.DFM} 

function Load(Path, Key: string): string; 
var 
  Reg: TRegistry; 
begin 
  Reg := TRegistry.Create; 
  try 
    Reg.RootKey := HKEY_CURRENT_USER; 
    Reg.OpenKey(Path, False); 
    try 
      Result := Reg.ReadString(Key); 
    except 
      Result := ''; 
    end; 
    Reg.CloseKey; 
  finally 
    Reg.Free; 
  end; 
end; 

function WinDir: string; 
var 
  WinDir: PChar; 
begin 
  WinDir := StrAlloc(MAX_PATH); 
  GetWindowsDirectory(WinDir, MAX_PATH); 
  Result := string(WinDir); 
  if Result[Length(Result)] <> '\' then 
    Result := Result + '\'; 
  StrDispose(WinDir); 
end; 

function GetSysDir: string; 
var 
  dir: array [0..MAX_PATH] of Char; 
begin 
  GetSystemDirectory(dir, MAX_PATH); 
  Result := StrPas(dir); 
end; 

// Navigate to a page 

procedure TForm1.Button1Click(Sender: TObject); 
begin 
  Webbrowser1.Navigate(edit1.Text); 
end; 

// Add the current page to the favorites 

procedure TForm1.Button2Click(Sender: TObject); 
var 
  url: TStringList; 
  fav: string; 
  title, b: string; 
  i: Integer; 
  c: Char; 
begin 
  fav := Load('Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders','Favorites'); 
  url := TStringList.Create; 
  try 
    url.Add('[InternetShortcut]'); 
    url.Add('URL=' + webbrowser1.LocationURL); 
    url.Add('WorkingDirectory=' + WinDir()); 
    url.Add('IconIndex=0'); 
    url.Add('ShowCommand=7'); 
    url.Add('IconFile=' + GetSysDir() + '\url.dll'); 
    title := Webbrowser1.LocationName; 
    b := ''; 
    for i := 1 to Length(title) do 
    begin 
      c := title[i]; 
      if not (c in NotAllowed) then 
      begin 
        b := b + Webbrowser1.LocationName[i]; 
      end; 
    end; 
    url.SaveToFile(fav + '\' + b + '.url'); 
  finally 
    url.Free; 
  end; 
end; 

end.
Взято с сайта



Как добавить True Type шрифт в систему?


Как добавить True Type шрифт в систему?



Чтобы установить шрифт в систему, необходимо скопировать файл шрифта в 'Windows\Fonts' и добавить ключ в реестр:

'Software\Microsoft\Windows\CurrentVersion\Fonts'

Этот ключ указывает на файл шрифта. Далее запускаем API функцию 'AddFontRecource'. В заключении нужно уведомить систему широковещательным сообщением.


uses Registry; 

procedure TForm1.Button1Click(Sender: TObject); 
var 
  hReg: TRegistry; 
  hBool : bool; 
begin 
  CopyFile('C:\DOWNLOAD\FP000100.TTF', 
           'C:\WINDOWS\FONTS\FP000100.TTF', hBool); 
  hReg := TRegistry.Create; 
  hReg.RootKey := HKEY_LOCAL_MACHINE; 
  hReg.LazyWrite := false; 
  hReg.OpenKey('Software\Microsoft\Windows\CurrentVersion\Fonts', 
               false); 
  hReg.WriteString('TESTMICR (TrueType)','FP000100.TTF'); 
  hReg.CloseKey; 
  hReg.free; 
  //Добавляем ресурс шрифта
  AddFontResource('c:\windows\fonts\FP000100.TTF'); 
  SendMessage(HWND_BROADCAST, WM_FONTCHANGE, 0, 0); 
  //Убираем блокировку ресурса
  RemoveFontResource('c:\windows\fonts\FP000100.TTF'); 
  SendMessage(HWND_BROADCAST, WM_FONTCHANGE, 0, 0); 
end;

Взято с Исходников.ru



Как добавить True Type шрифт в систему?




Чтобы установить шрифт в систему, необходимо скопировать файл шрифта в 'Windows\Fonts' и добавить ключ в реестр:

'Software\Microsoft\Windows\CurrentVersion\Fonts'

Этот ключ указывает на файл шрифта. Далее запускаем API функцию 'AddFontRecource'. В заключении нужно уведомить систему широковещательным сообщением.

uses Registry; 

procedure TForm1.Button1Click(Sender: TObject); 
var 
  hReg: TRegistry; 
  hBool : bool; 
begin 
  CopyFile('C:\DOWNLOAD\FP000100.TTF', 
           'C:\WINDOWS\FONTS\FP000100.TTF', hBool); 
  hReg := TRegistry.Create; 
  hReg.RootKey := HKEY_LOCAL_MACHINE; 
  hReg.LazyWrite := false; 
  hReg.OpenKey('Software\Microsoft\Windows\CurrentVersion\Fonts', 
               false); 
  hReg.WriteString('TESTMICR (TrueType)','FP000100.TTF'); 
  hReg.CloseKey; 
  hReg.free; 
  //Добавляем ресурс шрифта
  AddFontResource('c:\windows\fonts\FP000100.TTF'); 
  SendMessage(HWND_BROADCAST, WM_FONTCHANGE, 0, 0); 
  //Убираем блокировку ресурса
  RemoveFontResource('c:\windows\fonts\FP000100.TTF'); 
  SendMessage(HWND_BROADCAST, WM_FONTCHANGE, 0, 0); 
end;



Взято из





Как добавлять колонки в обычный Listbox (TListbox)?


Как добавлять колонки в обычный Listbox (TListbox)?



Класс TListbox содержит свойство TabWith:

ListBox1.TabWith := 50; 
ListBox1.Items.Add('Column1'^I'Column2');  // ^I это символ Tab

Взято с Исходников.ru



Как добиться верной работы фильтра на запросах и на неиндексированных таблицах


Как добиться верной работы фильтра на запросах и на неиндексированных таблицах




Автор: Nomadic

(Т.е. при работе программы наблюдалась следующая картина: в результате очередной фильтрации оставалось видно 4 записи из восьми. Добавляем букву к фильтру, остается, допустим, две. Убираем букву, которую только что добавили, в гриде все равно видно только две записи)

Эта проблема была в Delphi 3.0 только на TQuery, а в Delphi 3.01 появилась и в TTable. Лечится так (простой пример):



procedureTMainForm.Edit1Change(Sender: TObject);
begin
  if length(Edit1.Text) > 0 then
  begin
    Table1.Filtered := TRUE;
    UpdateFilter(Table1);
  end
  else
    Table1.Filtered := FALSE;
end;

procedure TMainForm.UpdateFilter(DataSet: TDataSet);
var
  FR: TFilterRecordEvent;
begin
  with DataSet do
  begin
    FR := OnFilterRecord;
    if Assigned(FR) and Active then
    begin
      DisableControls;
      try
        OnFilterRecord := nil;
        OnFilterRecord := FR;
      finally
        EnableControls;
      end;
    end;
  end;
end;



Взято из





Как добраться до конкретного фрейма?


Как добраться до конкретного фрейма?





var

  HTML_Doc: IHTMLDocument2;
  Window: IHTMLWindow2;
  oRange1: variant;
  name_frame: OleVariant;

  HTML_Doc := WebBrowser1.Document as IHTMLDocument2;
  Window := HTML_Doc.parentWindow as IHTMLWindow2;
  name_frame := 'mainFrame';
  oRange1 := Window.frames.item(name_frame).document.body.createTextRange;

Автор ответа: Good Man
Взято с Vingrad.ru




Как долго запущена Windows?


Как долго запущена Windows?



Ниже приведён код обработчика события OnClick для Button1. Он показывает диалоговое окошко с текстом в следующем формате

Windows started on Thursday, February 10, 2000 at 11:42:46 AM
Its been up for 0 days, 3 hours, 22 minutes, 54 seconds

procedure TForm1.Button1Click(Sender: TObject); 
var 
  ndays: double; 
  ticks: LongInt; 
  btime: TDateTime; 
begin 
 {Функция GetTickCount получает количество миллисекунд, прошедших с момента старта Windows}
  ticks := GetTickCount; 

  {Чтобы получить дни, необходимо разделить на количество миллисекунд в дне, 24*60*60*1000=86400000} 
  ndays := ticks/86400000; 

  {теперь вычитаем из текущей даты полученное количество дней работы Windows}
  bTime := now-ndays; 

  {показываем диалоговое окошко с сообщением}
  ShowMessage( 
   FormatDateTime('"Windows started on" dddd, mmmm d, yyyy, ' + 
                  '"at" hh:nn:ss AM/PM', bTime) + #10#13 + 
   'Its been up for ' + IntToStr(Trunc(nDays)) + ' days,' + 
   FormatDateTime(' h "hours," n "minutes," s "seconds"',ndays)); 
end;

Взято с Исходников.ru




Как форматировать диск?


Как форматировать диск?




unit Unit1;
interface

uses
  SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls,
  Forms, Dialogs, StdCtrls;

type
  TUndocSHFormat = class(TForm)
    Label1: TLabel;
    Combo1: TComboBox;
    cmdSHFormat: TButton;
    cmdEnd: TButton;
    lbMessage: TLabel;
    procedure FormCreate(Sender: TObject);
    procedure cmdSHFormatClick(Sender: TObject);
    procedure cmdEndClick(Sender: TObject);
  private
    procedure LoadAvailableDrives;
  public
  end;

var
  UndocSHFormat: TUndocSHFormat;

implementation

{$R *.DFM}

type POSVERSIONINFO = ^TOSVERSIONINFO;
  TOSVERSIONINFO = record
    dwOSVersionInfoSize: Longint;
    dwMajorVersion: Longint;
    dwMinorVersion: Longint;
    dwBuildNumber: Longint;
    dwPlatformId: Longint;
    szCSDVersion: PChar;
  end;

function GetVersionEx(lpVersionInformation: POSVERSIONINFO): Longint; stdcall; external 'kernel32.dll' name 'GetVersionExA';

const VER_PLATFORM_WIN32s = 0;
const VER_PLATFORM_WIN32_WINDOWS = 1;
const VER_PLATFORM_WIN32_NT = 2;


function SHFormatDrive(hwndOwner: longint; iDrive: Longint; iCapacity: LongInt;
  iFormatType: LongInt): Longint;
  stdcall; external 'shell32.dll';

const SHFD_CAPACITY_DEFAULT = 0;
const SHFD_CAPACITY_360 = 3;
const SHFD_CAPACITY_720 = 5;

//Win95
//Const SHFD_FORMAT_QUICK = 0;
//Const SHFD_FORMAT_FULL = 1;
//Const SHFD_FORMAT_SYSONLY = 2;

//WinNT
//Public Const SHFD_FORMAT_FULL = 0
//Public Const SHFD_FORMAT_QUICK = 1

const SHFD_FORMAT_QUICK: LongInt = 0;
const SHFD_FORMAT_FULL: LongInt = 1;
const SHFD_FORMAT_SYSONLY: LongInt = 2;

function GetLogicalDriveStrings(nBufferLength: LongInt; lpBuffer: PChar): LongInt;
  stdcall; external 'kernel32.dll' name 'GetLogicalDriveStringsA';

function GetDriveType(nDrive: PChar): LongInt;
  stdcall; external 'kernel32.dll' name 'GetDriveTypeA';

const DRIVE_REMOVABLE = 2;
const DRIVE_FIXED = 3;
const DRIVE_REMOTE = 4;
const DRIVE_CDROM = 5;
const DRIVE_RAMDISK = 6;

function IsWinNT: Boolean;
var osvi: TOSVERSIONINFO;
begin
  osvi.dwOSVersionInfoSize := SizeOf(osvi);
  GetVersionEx(@osvi);
  IsWinNT := (osvi.dwPlatformId = VER_PLATFORM_WIN32_NT);
end;


function GetDriveDisplayString(currDrive: PChar): pchar;
begin
  GetDriveDisplayString := nil;
  case GetDriveType(currDrive) of
    0, 1: GetDriveDisplayString := ' - Undetermined Drive Type -';
    DRIVE_REMOVABLE:
      case currDrive[1] of
        'A', 'B': GetDriveDisplayString := 'Floppy drive';
      else
        GetDriveDisplayString := 'Removable drive';
      end;
    DRIVE_FIXED: GetDriveDisplayString := 'Fixed (Hard) drive';
    DRIVE_REMOTE: GetDriveDisplayString := 'Remote drive';
    DRIVE_CDROM: GetDriveDisplayString := 'CD ROM';
    DRIVE_RAMDISK: GetDriveDisplayString := 'Ram disk';
  end;
end;

procedure TUndocSHFormat.LoadAvailableDrives;
var
  a, r: LongInt;
  lpBuffer: array[0..256] of char;
  currDrive: array[0..256] of char;
  lpDrives: pchar;

begin
  getmem(lpDrives, 256);
  fillchar(lpBuffer, 64, #32);

  r := GetLogicalDriveStrings(255, lpBuffer);

  if r <> 0 then
    begin
      strlcopy(lpBuffer, lpBuffer, r);
      for a := 0 to r do
        lpDrives[a] := lpBuffer[a];
      lpBuffer[r + 1] := #0;
      repeat
        strlcopy(currDrive, lpDrives, 3);
        lpDrives := @lpDrives[4];
        Combo1.Items.Add(strpas(currDrive) + ' ' + GetDriveDisplayString(currDrive));
      until lpDrives[0] = #0;
    end;
end;


procedure TUndocSHFormat.FormCreate(Sender: TObject);
begin
  lbMessage.caption := '';
  LoadAvailableDrives;
  Combo1.ItemIndex := 0;
  if IsWinNT then
    begin
      SHFD_FORMAT_FULL := 0;
      SHFD_FORMAT_QUICK := 1;
    end
  else //it's Win95
    begin
      SHFD_FORMAT_QUICK := 0;
      SHFD_FORMAT_FULL := 1;
      SHFD_FORMAT_SYSONLY := 2;
    end;
end;

procedure TUndocSHFormat.cmdSHFormatClick(Sender: TObject);
var
  resp: Integer;
  drvToFormat: Integer;
  prompt: string;
begin
  drvToFormat := Combo1.ItemIndex;
  prompt := 'Are you sure you want to run the Format dialog against ' + Combo1.Text;

  if drvToFormat > 0 then
    resp := MessageDLG(prompt, mtConfirmation, [mbYes, mbNo], 0)
  else
    resp := mrYes;

  if resp = mrYes then
    begin
      lbMessage.Caption := 'Checking drive for disk...';
      Application.ProcessMessages;
      SHFormatDrive(handle, drvToFormat, SHFD_CAPACITY_DEFAULT, SHFD_FORMAT_QUICK);
      lbMessage.caption := '';
    end;
end;

procedure TUndocSHFormat.cmdEndClick(Sender: TObject);
begin
  close;
end;

end.

Автор ответа: Baa
Взято с Vingrad.ru