Программирование на Delphi - обмен опытом / Графика

© Зайцев Олег 1998-2004
Лучшая портативная техника. Плееры Камеры Телефоны Компьютеры
Покупателям, пришедшим на www.porta.ru по этой ссылке - дополнительная скидка 1%
Железо | Система | WEB | Компоненты | Графика | Ссылки | Мультимедиа | Сети | Прочее | Реестр | Литература

Статистика

Графика

Советы по работе с графикой

Рекомендую:
Главная страница \ Графика

  • Общие вопросы
  • Работа с регионами

    Общие вопросы

    Как работать с палитрой в Delphi? На форме установлен TImage и видна картинка (*.BMP файл), как изменить у него палитру цветов ? * * Задать вопрос Наверх
    Палитра в TBitmap и TMetaFile доступна через property Palette. Если палитра имеется (что совсем необязательно), то Palette<>0:
    procedure TMain.BitBtnClick(Sender: TObject);
    var
      Palette : HPalette;
      PaletteSize : Integer;
      LogSize: Integer;
      LogPalette: PLogPalette;
      Red : Byte;
    begin
      Palette := Image.Picture.Bitmap.ReleasePalette;
      // здесь можно использовать просто Image.Picture.Bitmap.Palette, но  я не
      // знаю, удаляются ли ненужные палитры автоматически
    
      if Palette=0 then exit; //Палитра отсутствует
      PaletteSize := 0;
      if GetObject(Palette, SizeOf(PaletteSize), @PaletteSize) = 0 then Exit;
      // Количество элементов в палитре = paletteSize
      if PaletteSize = 0 then Exit; // палитра пустая
      // определение размера палитры
      LogSize := SizeOf(TLogPalette) + (PaletteSize - 1) * SizeOf(TPaletteEntry);
      GetMem(LogPalette, LogSize);
      try
        // заполнение полей логической палитры
        with LogPalette^ do begin
          palVersion := $0300;    palNumEntries := PaletteSize;
          GetPaletteEntries(Palette, 0, PaletteSize, palPalEntry);
          // делаете что нужно с палитрой, например:
          Red := palPalEntry[PaletteSize-1].peRed;
          Edit1.Text := 'Красная составляющего последнего элемента  палитры ='+IntToStr(Red);
          palPalEntry[PaletteSize-1].peRed := 0;
          //.......................................
        end;
        // завершение работы
        Image.Picture.Bitmap.Palette := CreatePalette(LogPalette^);
      finally
        FreeMem(LogPalette, LogSize);
        // я должен позаботиться сам об удалении Released Palette
        DeleteObject(Palette);
      end;
    end;
    
    
    { Этот модуль заполняет фон формы рисунком bor6.bmp (256 цветов) 
      и меняет его палитру при нажатии кнопки }
    unit bmpformu;
    interface
    uses
      Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls;
    
    type
      TBmpForm = class(TForm)
        Button1: TButton;
        procedure FormDestroy(Sender: TObject);
        procedure FormPaint(Sender: TObject);
        procedure Button1Click(Sender: TObject);
        procedure FormCreate(Sender: TObject);
      private
        Bitmap: TBitmap;
        procedure ScrambleBitmap;
        procedure WMEraseBkgnd(var m: TWMEraseBkgnd); message WM_ERASEBKGND;
    end;
    
    var
      BmpForm: TBmpForm;
    
    implementation
    {$R *.DFM}
    procedure TBmpForm.FormCreate(Sender: TObject);
    begin
      Bitmap := TBitmap.Create;
      Bitmap.LoadFromFile('bor6.bmp');
    end;
    
    procedure TBmpForm.FormDestroy(Sender: TObject);
    begin
      Bitmap.Free;
    end;
    
    // since we're going to be painting the whole form, handling this
    // message will suppress the uneccessary repainting of the background
    // which can result in flicker.
    procedure TBmpform.WMEraseBkgnd(var m : TWMEraseBkgnd);
    begin
      m.Result := LRESULT(False);
    end;
    
    procedure TBmpForm.FormPaint(Sender: TObject);
    var x, y: Integer;
    begin
      y := 0;
      while y < Height do begin
        x := 0;
        while x < Width do begin
          Canvas.Draw(x, y, Bitmap);
          x := x + Bitmap.Width;
        end;
        y := y + Bitmap.Height;
      end;
    end;
    
    procedure TBmpForm.Button1Click(Sender: TObject);
    begin
      ScrambleBitmap; Invalidate;
    end;
    
    // scrambling the bitmap is easy when it's has 256 colors:
    // we just need to change each of the color in the palette
    // to some other value.
    procedure TBmpForm.ScrambleBitmap;
    var
      pal: PLogPalette;
      hpal: HPALETTE;
      i: Integer;
    begin
      pal := nil;
      try
        GetMem(pal, sizeof(TLogPalette) + sizeof(TPaletteEntry) * 255);
        pal.palVersion := $300;
        pal.palNumEntries := 256;
        for i := 0 to 255 do
        begin
          pal.palPalEntry[i].peRed := Random(255);
          pal.palPalEntry[i].peGreen := Random(255);
          pal.palPalEntry[i].peBlue := Random(255);
        end;
        hpal := CreatePalette(pal^);
        if hpal <> 0 then
          Bitmap.Palette := hpal;
      finally
        FreeMem(pal);
      end;
    end;
    
    end.
    

    Как заполнить Canvas рисунком с рабочего стола, учитывая координаты. * * Задать вопрос Наверх
    Для заполнения Canvas рисунком с рабочего стола неоьходимо использовать вызов API Function PaintDesktop(HDC) : boolean;
    Например: PaintDesktop(form1.Canvas.Handle);

    Как изменить вид ListBox (вставить изображения, изменить шрифт отдельных элеметов и т.п.) ? * * Задать вопрос Наверх
    Для вставки растровых изображений в ListBox необходимо установить в инспекторе объектов поле Style равным lbOwnerDrawFixed (при фиксированной высоте строки), или в lbOwnerDrawVariable (при переменной). Затем следует создать собственный обработчик события для OnDrawItem. Этот обработчик вызывается всякий раз, когда возникает необходимость в отрисовке элемента списка.
    Обработчик имеет вид

    ListBox1DrawItem(Control: TWinControl; Index: Integer; 
               Rect: TRect; State: DrawState); 
    
    где Control - указатель на ListBox, вызвавший событие; Index - индекс элемента, который необходимо отобразить; Rect - координатный прямоугольник, в котором следует рисовать элемент; State - состояние элемента
    Пример:
    Рисуются изображения размером 32*16 (размер стандартной картинки для Delphi). Очень полезно при поиске нужного изображения для кнопок!
    Установить в инспекторе объектов для ListBox поле ItemHeight = 19, а поле Color = clBtnFace.
    // Загрузить список файлов в ListBox1 при нажатии на кнопку Load (например)
    procedure TForm1.bLoadClick(Sender: TObject);
    VAR S : String; 
    begin 
      ListBox1.Clear; // Очистка списка
      S := '*.bmp'#0; // Определение шаблона
      // Заполнение списка 
      ListBox1.Perform(LB_DIR, DDL_ReadWrite, Longint(@S[1])); 
    end; 
              ............ 
    
    // Отобразжение Элемента списка
    procedure TForm1.ListBox1DrawItem(Control: TWinControl; Index: Integer; 
               Rect: TRect; State: DrawState); 
    VAR 
      Bitmap : TBitmap; // Картинка
      Offset : Integer; 
      BMPRect: TRect; 
    begin 
      WITH (Control AS TListBox).Canvas DO BEGIN 
        FillRect(Rect); 
        Bitmap := TBitmap.Create;
        // Попытка загрузки картинки
        Bitmap.LoadFromFile(ListBox1.Items[Index]); 
        Offset := 0; 
        // Загрузилась ?
        IF Bitmap <> NIL THEN BEGIN 
          BMPRect := Bounds(Rect.Left+2, Rect.Top+2, 
                            (Rect.Bottom-Rect.Top-2)*2, Rect.Bottom-Rect.Top-2); 
          { StretchDraw(BMPRect, Bitmap); Можно просто нарисовать, но лучше сначала убрать фон} 
          BrushCopy(BMPRect,Bitmap, Bounds(0, 0, Bitmap.Width, Bitmap.Height),
                    Bitmap.Canvas.Pixels[0, Bitmap.Height-1]); 
          Offset := (Rect.Bottom-Rect.Top+1)*2; 
        END; 
        // Вывод текста элемента
        TextOut(Rect.Left+Offset, Rect.Top, ListBox1.Items[Index]); 
        Bitmap.Free; 
       END; 
    end; 
    
    Данный пример работает медленно, но оптимизация, для ускорения, вызвала бы трудность в понимании общего принципа его работы.

    Как из программы на Delphi рисовать в любой части экрана или в чужом окне? * * Задать вопрос Наверх
    Для этого надо воспользоваться функциями API. Получить контекст чужого окна, либо всего экрана можно при помощи функции function GetDC(Wnd: HWnd): HDC;
    где Wnd - указатель на нужное окно, или 0 для получения контекста всего экрана.
    Затем, получив контекст, можно приступить к рисованию посредством функций API
    Пример:

    PROCEDURE DrawOnScreen; 
    VAR ScreenDC: hDC; 
    BEGIN 
      ScreenDC := GetDC(0); // получить контекст экрана
      Ellipse(ScreenDC, 0, 0, 200, 200); // Нарисовали круг
      ReleaseDC(0,ScreenDC); // освободили контекст
    END; 
    
    Не забывайте после своих манипуляций посылать пострадавшим (или всем) окнам сообщение о необходимости перерисовки для восстановления их первоначального вида.

    Написание текста под углом * * Задать вопрос Наверх
    В Delphi можно достаточно просто выводить текст под любым углом, единственное условие - применяемый шрифт должен быть TrueType

    // Эта процедура устанавливает угол вывода текста для указанного Canvas, 
    // угол задается в градусах
    procedure CanvasSetTextAngle(c: TCanvas; d: single);
    var   LogRec: TLOGFONT;     // Информация о шрифте
    begin
     // Читаем текущюю информацию о шрифте
     GetObject(c.Font.Handle,SizeOf(LogRec) ,Addr(LogRec) );
     // Изменяем угол
     LogRec.lfEscapement := round(d*10);
     // Устанавливаем новые параметры
     c.Font.Handle := CreateFontIndirect(LogRec);
    end;
    

    Преобразование цвета RGB в HLS (оттенок,яркость,насыщенность) и наоборот * * Задать вопрос Наверх

    { Максимальные значения }
    Const
     HLSMAX = 240;
     RGBMAX = 255;
     UNDEFINED = (HLSMAX*2) div 3;
    Var
     H, L, S  : integer; { H-оттенок, L-яркость, S-насыщенность }
     R, G, B  : integer; { цвета }
    
    procedure RGBtoHLS;
    Var
     cMax,cMin  : integer;
     Rdelta,Gdelta,Bdelta : single;
    Begin
       cMax := max( max(R,G), B);
       cMin := min( min(R,G), B);
       L := round( ( ((cMax+cMin)*HLSMAX) + RGBMAX )/(2*RGBMAX) );
    
       if (cMax = cMin) then begin
          S := 0; H := UNDEFINED;
       end else begin
          if (L <= (HLSMAX/2)) then
             S := round( ( ((cMax-cMin)*HLSMAX) + ((cMax+cMin)/2) ) / (cMax+cMin) )
          else
             S := round( ( ((cMax-cMin)*HLSMAX) + ((2*RGBMAX-cMax-cMin)/2) )
                / (2*RGBMAX-cMax-cMin) );
          Rdelta := ( ((cMax-R)*(HLSMAX/6)) + ((cMax-cMin)/2) ) / (cMax-cMin);
          Gdelta := ( ((cMax-G)*(HLSMAX/6)) + ((cMax-cMin)/2) ) / (cMax-cMin);
          Bdelta := ( ((cMax-B)*(HLSMAX/6)) + ((cMax-cMin)/2) ) / (cMax-cMin);
          if (R = cMax) then H := round(Bdelta - Gdelta)
          else if (G = cMax) then H := round( (HLSMAX/3) + Rdelta - Bdelta)
          else H := round( ((2*HLSMAX)/3) + Gdelta - Rdelta );
          if (H < 0) then H:=H + HLSMAX;
          if (H > HLSMAX) then H:= H - HLSMAX;
       end;
       if S<0 then S:=0; if S>HLSMAX then S:=HLSMAX;
       if L<0 then L:=0; if L>HLSMAX then L:=HLSMAX;
    end;
    
    
    procedure HLStoRGB;
    Var
     Magic1,Magic2 : single;
    
      function HueToRGB(n1,n2,hue : single) : single;
      begin
         if (hue < 0) then hue := hue+HLSMAX;
         if (hue > HLSMAX) then hue:=hue -HLSMAX;
         if (hue < (HLSMAX/6)) then
            result:= ( n1 + (((n2-n1)*hue+(HLSMAX/12))/(HLSMAX/6)) )
         else
         if (hue < (HLSMAX/2)) then result:=n2 else
         if (hue < ((HLSMAX*2)/3)) then
            result:= ( n1 + (((n2-n1)*(((HLSMAX*2)/3)-hue)+(HLSMAX/12))/(HLSMAX/6)))
         else result:= ( n1 );
      end;
    
    begin
       if (S = 0) then begin
          B:=round( (L*RGBMAX)/HLSMAX ); R:=B; G:=B;
       end else begin
          if (L <= (HLSMAX/2)) then Magic2 := (L*(HLSMAX + S) + (HLSMAX/2))/HLSMAX
          else Magic2 := L + S - ((L*S) + (HLSMAX/2))/HLSMAX;
          Magic1 := 2*L-Magic2;
          R := round( (HueToRGB(Magic1,Magic2,H+(HLSMAX/3))*RGBMAX + (HLSMAX/2))/HLSMAX );
          G := round( (HueToRGB(Magic1,Magic2,H)*RGBMAX + (HLSMAX/2)) / HLSMAX );
          B := round( (HueToRGB(Magic1,Magic2,H-(HLSMAX/3))*RGBMAX + (HLSMAX/2))/HLSMAX );
       end;
       if R<0 then R:=0; if R>RGBMAX then R:=RGBMAX;
       if G<0 then G:=0; if G>RGBMAX then G:=RGBMAX;
       if B<0 then B:=0; if B>RGBMAX then B:=RGBMAX;
    end;
    

    Как определить число цветов для текущих настроек экрана * * Задать вопрос Наверх
    Определить число цветов для текущих настроек экрана достаточно просто при помощи функции API GetDeviceCaps. Например, описанная ниже функция возвращает число бит на точку у данного компьютера. Так, например, 8 - 8 бит (256 цветов), 4 - 4 бит (16 цветов) и т.п. Напоминаю, что число цветов легко получить как 2^N, где N - число бит на точку.

    function GetDisplayColors : integer;
    var tHDC  : hdc;
    begin
     tHDC:=GetDC(0);
     result:=GetDeviceCaps(tHDC, 12)* GetDeviceCaps(tHDC, 14);
     ReleaseDC(0, tHDC);
    end;
    

    Как скопировать экран (или часть экрана) ? * * Задать вопрос Наверх
    Копирование экрана легко произвести при помощи функций API. Приведенная ниже библиотека позволит упростить эту операцию

    unit ScrnCap;
    interface
    uses WinTypes, WinProcs, Forms, Classes, Graphics, Controls;
    
    // Копирует прямоугольную область экрана
    function CaptureScreenRect(ARect : TRect) : TBitmap;
    // Копирование всего экрана
    function CaptureScreen : TBitmap;
    // Копирование клиентской области формы или элемента
    function CaptureClientImage(Control : TControl) : TBitmap;
    // Копирование всей формы элемента
    function CaptureControlImage(Control : TControl) : TBitmap;
    
    implementation
    // Получение системной палитры
    function GetSystemPalette : HPalette;
    var
     PaletteSize  : integer;
     LogSize      : integer;
     LogPalette   : PLogPalette;
     DC           : HDC;
     Focus        : HWND;
    begin
     result:=0;
     Focus:=GetFocus;
     DC:=GetDC(Focus);
     try
       PaletteSize:=GetDeviceCaps(DC, SIZEPALETTE);
       LogSize:=SizeOf(TLogPalette)+(PaletteSize-1)*SizeOf(TPaletteEntry);
       GetMem(LogPalette, LogSize);
       try
         with LogPalette^ do
         begin
           palVersion:=$0300;
           palNumEntries:=PaletteSize;
           GetSystemPaletteEntries(DC, 0, PaletteSize, palPalEntry);
         end;
         result:=CreatePalette(LogPalette^);
       finally
         FreeMem(LogPalette, LogSize);
       end;
     finally
       ReleaseDC(Focus, DC);
     end;
    end;
    
    // Копирование прямоугольной области
    function CaptureScreenRect(ARect : TRect) : TBitmap;
    var
     ScreenDC : HDC;
    begin
     Result:=TBitmap.Create;
     with result, ARect do begin
      Width:=Right-Left;
      Height:=Bottom-Top;
      ScreenDC:=GetDC(0);
      try
        BitBlt(Canvas.Handle, 0,0,Width,Height,ScreenDC, Left, Top, SRCCOPY	);
      finally
        ReleaseDC(0, ScreenDC);
      end;
      Palette:=GetSystemPalette;
     end;
    end;
    
    // Копирование всего экрана
    function CaptureScreen : TBitmap;
    begin
     with Screen do
      Result:=CaptureScreenRect(Rect(0,0,Width,Height));
    end;
    
    function CaptureClientImage(Control : TControl) : TBitmap;
    begin
     with Control, Control.ClientOrigin do
      result:=CaptureScreenRect(Bounds(X,Y,ClientWidth,ClientHeight));
    end;
    
    function CaptureControlImage(Control : TControl) : TBitmap;
    begin
     with Control do
      if Parent=Nil then
        result:=CaptureScreenRect(Bounds(Left,Top,Width,Height))
      else
       with Parent.ClientToScreen(Point(Left, Top)) do
        result:=CaptureScreenRect(Bounds(X,Y,Width,Height));
    end;
    end.
    

    Отображение текста со стилем "disabled" (характерный серый контурный текст) * * Задать вопрос Наверх

    {************************ Draw Disabled Text **************
     ***** This function draws text in "disabled" style.  *****
     ***** i.e. the text is grayed .                      *****
     **********************************************************}
    function DrawDisabledText (Canvas : tCanvas; Str: PChar; Count: Integer;
                               var Rect: TRect;  Format: Word): Integer;
    begin
      SetBkMode(Canvas.Handle, TRANSPARENT);
    
      OffsetRect(Rect, 1, 1);
      Canvas.Font.color:= ClbtnHighlight;
      DrawText (Canvas.Handle, Str, Count, Rect,Format);
    
      Canvas.Font.Color:= ClbtnShadow;
      OffsetRect(Rect, -1, -1);
      DrawText (Canvas.Handle, Str, Count, Rect, Format);
    end;
    

    Как изменять разрешение экрана во время выполнения программы * * Задать вопрос Наверх

    // Переход в полноэкранный режим
    function SetFullscreenMode:Boolean;
    var DeviceMode : TDevMode;
    begin
     with DeviceMode do begin
      dmSize:=SizeOf(DeviceMode);
      dmBitsPerPel:=16;
      dmPelsWidth:=640;
      dmPelsHeight:=480;
      dmFields:=DM_BITSPERPEL or DM_PELSWIDTH or DM_PELSHEIGHT;
      result:=False;
      if ChangeDisplaySettings(DeviceMode,CDS_TEST or CDS_FULLSCREEN) <> DISP_CHANGE_SUCCESSFUL 
       then Exit;
      Result:=ChangeDisplaySettings(DeviceMode,CDS_FULLSCREEN) = DISP_CHANGE_SUCCESSFUL;
     end;
    end;
    
    // Возврат в исходный режим
    procedure RestoreDefaultMode;
    var T : TDevMode absolute 0;
    begin
     ChangeDisplaySettings(T,CDS_FULLSCREEN);
    end;
    
    procedure TForm1.Button1Click(Sender: TObject);
    begin
     if setFullScreenMode then begin
      sleep(7000);
      RestoreDefaultMode;
     end;
    end;
    

    Как поместить картинку из базы данных в компонент TIMAGE ? * * Задать вопрос Наверх
    1) Предполагается, что поле имеет тип BLOB (например, с именем Pict)
    2) в запросе Query.SQL пишется что-то вроде
    'select Pict from sometable where somefield=somevalue'
    3) запрос открывается
    4) делается "присваивание":
    Image1.Picture.Assing(TBlobField(Query.FieldByName('Pict'))
    или, если известно, что эта картинка - Bitmap, то можно
    Image1.Picture.Bitmap.Assing(TBlobField(Query.FieldByName('Pict'))

    Как установить произвольный цвет, зная его RGB * * Задать вопрос Наверх
    Самый простой путь - присвоение значения свойству Color, которое представляет собой значение LongInt, байты которого кодируют цвета в следующем порядке BGR. Пример - установка красного цвета - Color := $0000FF. Причем обычно принято писать незначащие нули - это упрощает чтение и понимание принципа кодирования


    © Зайцев Олег, "Программирование на Delphi - обмен опытом" 1999-2004. При использовании любых материалов данного сайта необходимо указывать источник информации. Дата обновления: 22.11.2004. Сайт размещен на хостинге AGAVA - Хостинг от AGAVA.ru