Jak narysować krzyżyk w obrazku?

0

Witam,

tak jak w temacie, mam Image1 a w nim zaczytany z pliku obrazek jpeg o wymiarach 200x150.
W jaki sposób po jego środku narysować czerwony krzyżyk, i zapisać zmodyfikowany obrazek?

Do rysowania krzyżyka na ekranie korzystam z funkcji autorstwa @kAzek:

procedure TfrmMenu.PaintX(p: TPoint);
var
  hWnd ,hButton: Windows.HWND;
  hDC: Windows.HDC;
  hPen, hOldPen: Windows.HPEN;
  r: TRect;
begin
  hWnd := GetDesktopWindow;
  hButton :=  GetDC(hButton);

  //rysowanie
  hDC:= GetDC(hButton);

  //bedziemy rysowac ciagla linie 3px czerwona dlatego tworzymy sobie takie pioro
  hPen:= CreatePen(PS_SOLID, 3, RGB(255, 0, 0));
  hOldPen:= SelectObject(hDC, hPen);  //i je ustawiamy

  //bardziej tego rysowania nie dalalo sie skomplikowac ale nie chcialo mi sie liczyc ;)
  MoveToEx(hDC, p.X, p.Y, nil); //na srodek
  LineTo(hDC, p.X, p.Y - 10);  //ramie krzyża ma 10px
  MoveToEx(hDC, p.X, p.Y, nil); //na srodek
  LineTo(hDC, p.X, p.Y + 10);
  MoveToEx(hDC, p.X, p.Y, nil); //na srodek
  LineTo(hDC, p.X - 10, p.Y);
  MoveToEx(hDC, p.X, p.Y, nil); //na srodek
  LineTo(hDC, p.X + 10, p.Y);

  //robimy po sobie porzadek
  SelectObject(hDC, hOldPen);
  DeleteObject(hPen);
  ReleaseDC(hButton, hDC);

    Sleep(300); //czekamy dla demonstracji (po 2 sekundach zostanie wymazany)
//    Windows.InvalidateRect(hButton, nil, True); //tak mozesz wymazac
//  end;
end;
0

Tu nie musisz się bawić w mozolne rzeźbienie w WinAPI - rysuj bezpośrednio na kanwie obrazu, już bez WinAPI (skoro i tak korzystasz z VCL);

Przykład poniżej:

procedure TForm1.FormCreate(Sender: TObject);
begin
  // ustawienie ścieżki katalogu aplikacji (prywatne pole klasy)
  FAppPath := ExtractFilePath(ParamStr(0));
end;

procedure TForm1.btnLoadImageClick(Sender: TObject);
begin
  // załadowanie obrazka z pliku do komponentu
  imgSource.Picture.LoadFromFile(FAppPath + 'image.bmp');
end;

procedure TForm1.btnDrawCrossClick(Sender: TObject);
begin
  // narysowanie czerwonego krzyżyka na kanwie bitmapy
  with imgSource.Picture.Bitmap.Canvas do
  begin
    Pen.Color := clRed;
    MoveTo(25, 50);
    LineTo(75, 50);
    MoveTo(50, 25);
    LineTo(50, 75);
  end;
end;

procedure TForm1.btnSaveImageClick(Sender: TObject);
begin
  // zapisanie zmodyfikowanego obrazka do pliku źródłowego
  imgSource.Picture.SaveToFile(FAppPath + 'image.bmp');
end;

W załączniku aplikacja, w której użyty jest powyższy kod.

1

TJPGImage - wczytujesz obrazek
TBitmap - ustawiasz rozmiar jak u TJPGImage, kopiujesz obrazek z jpg do bmp
Rysujesz krzyżyk poprzez bitmap.canvas
Kopijesz obrzek z powrotem do jpg
zapisujesz obrazek.

2

Faktycznie, nie doczytałem, że to chodzi o obraz JPG...

W takim razie zmienia się jedynie ładowanie obrazu i jego zapis do pliku:

procedure TForm1.btnLoadImageClick(Sender: TObject);
var
  jpgSource: TJPEGImage;
begin
  jpgSource := TJPEGImage.Create();
  try
    jpgSource.LoadFromFile(FAppPath + 'image.jpg');
    imgSource.Picture.Bitmap.Assign(jpgSource);
  finally
    jpgSource.Free();
  end;
end;

procedure TForm1.btnSaveImageClick(Sender: TObject);
var
  jpgSource: TJPEGImage;
begin
  jpgSource := TJPEGImage.Create();
  try
    jpgSource.Assign(imgSource.Picture.Bitmap);
    jpgSource.SaveToFile(FAppPath + 'image.jpg');
  finally
    jpgSource.Free();
  end;
end;

Źródła poprawionej wersji w złączniku.

0

Dzięki za odpowiedź,

po modyfikacji funkcji tworzącej zrzut obrazu, kodu wygląda to tak:

function TfrmMenu.PrintScreen(ID_T: Integer; p: TPoint): WideString;
var
 can         :TCanvas;
 obrazekBMP  :TBitmap;
 obrazekJPG  :TJPEGImage;
 sz,wy :Integer;
Begin
  Result := '';
  can:= TCanvas.Create;
  can.Handle:= GetWindowDC(GetDesktopWindow); //przechwycenie uchwytu ekranu
  obrazekBMP:= TBitmap.Create;
  obrazekJPG:= TJPEGImage.Create;

  try
    try
     //parametry wycinka (okreslone miejsce na ekranie)
     sz:= 200; wy:= 150;

     obrazekBMP.Width:= sz;
     obrazekBMP.Height:= wy;
     obrazekBMP.Canvas.CopyRect(Rect(0, 0, sz, wy), Can, Rect(p.X - (sz div 2), p.Y - (wy div 2), p.X + sz - (sz div 2), p.Y + wy - (wy div 2)));

     with obrazekBMP.Canvas do
     begin
       Pen.Color := clRed;
       MoveTo(25, 50);
       LineTo(75, 50);
       MoveTo(50, 25);
       LineTo(50, 75);
     end;
     obrazekJPG.Assign(obrazekBMP);

     img_printscreen.Picture.Assign(Obrazekjpg);
     CreateDir(ExtractFileDir(Application.ExeName) + '\tmp');
     obrazekJPG.SaveToFile(ExtractFileDir(Application.ExeName) + '\tmp\' + IntToStr(ID_T) + '.jpeg');
     Result := ExtractFileDir(Application.ExeName) + '\tmp\' + IntToStr(ID_T) + '.jpeg';
    except
      on e:exception do
      begin
        ShowMessageDlg('Błąd zapisu zrzutu ekranu!#13#13<b>' + e.Message + '</b>', 'err');
      end;
    end;
  finally
   can.Free;
   obrazekBMP.Free;
   obrazekJPG.Free;
  end;
end;

Wszystko pięknie ładnie, ale nie rysuje krzyżyka na środku, tylko mocno przesunięty ;/

2

Bo masz wycinek, więc te liczby w MoveTo oraz LineTo musisz odpowiednio przesunąć.
Na pierwszy rzut oka o wartości: p.X - (sz div 2), p.Y - (wy div 2)

0

Jest to zrzut ekranu o wys 200x150 i na środku tego fragmentu musze narysować krzyżyk.
Próbuję przestawiając te wartości

p.X + sz - (sz div 2)

itp, ale nie mogę sobie poradzić.

Jesteście w stanie to dopasować, żeby rysował centralnie w środku w tej funkcji?

2

Albo odstaw ten projekt i zajmij się na razie podstawami, albo wynajmij fachowca, bo jak na razie dla ciebie każde pierdnięcie jest wielkim problemem.

2

@user322, nie wiem dlaczego tak się motasz z obliczeniem środka wycinka pulpitu - przecież to bardzo proste obliczenia; Zawsze możesz wspomóc się dodatkowymi stałymi, w których zapiszesz sobie wymiary wycinka i połowy wymiarów wycinka - będzie łatwiej użyć je w kodzie;

Łatwiej jest najpierw skopiować wycinek pulpitu, po czym na gotowym wycinku namalować krzyżyk; Poniżej masz zakomentowany przykład metody realizującej pobranie wycinka pulpitu, na którym rysowany jest biały krzyżyk w miejscu, w którym znajduje się kursor, po czym wycinek zapisywany jest do pliku JPG:

procedure TMainForm.CapturePartOfDesktop(const APoint: TPoint; const AFileName: TFileName);
const
  { part dimensions }
  PART_WIDTH  = Integer(200);
  PART_HEIGHT = Integer(150);
  { half of part dimensions }
  PART_HALF_WIDTH  = Integer(PART_WIDTH  shr 1);
  PART_HALF_HEIGHT = Integer(PART_HEIGHT shr 1);
const
  { cross arm width }
  CROSS_ARM_WIDTH = Integer(10);
var
  cnvDesktop: TCanvas;
  rctDesktop: TRect;
  bmpPart:    TBitmap;
  jpgPart:    TJPEGImage;
begin
  { create desktop and part classes }
  cnvDesktop := TCanvas.Create();
  bmpPart   := TBitmap.Create();
  try
    { get the desktop handle }
    cnvDesktop.Handle := GetWindowDC(GetDesktopWindow());
    { set the part dimensions }
    bmpPart.Width  := PART_WIDTH;
    bmpPart.Height := PART_HEIGHT;
    { set the part rect bounds in desktop }
    rctDesktop.Left   := APoint.X - PART_HALF_WIDTH;
    rctDesktop.Right  := APoint.X + PART_HALF_WIDTH;
    rctDesktop.Top    := APoint.Y - PART_HALF_HEIGHT;
    rctDesktop.Bottom := APoint.Y + PART_HALF_HEIGHT;
    { copy rect from desktop to part }
    bmpPart.Canvas.CopyRect(bmpPart.Canvas.ClipRect, cnvDesktop, rctDesktop);

    { paint the cross on part bitmap }
    with bmpPart.Canvas do
    begin
      { set the pen style }
      Pen.Color := clWhite;
      Pen.Width := 3;
      { draw the cross }
      MoveTo(PART_HALF_WIDTH,  PART_HALF_HEIGHT - CROSS_ARM_WIDTH);
      LineTo(PART_HALF_WIDTH,  PART_HALF_HEIGHT + CROSS_ARM_WIDTH);
      MoveTo(PART_HALF_WIDTH - CROSS_ARM_WIDTH, PART_HALF_HEIGHT);
      LineTo(PART_HALF_WIDTH + CROSS_ARM_WIDTH, PART_HALF_HEIGHT);
    end;

    { save part to file }
    jpgPart := TJPEGImage.Create();
    try
      jpgPart.Assign(bmpPart);
      jpgPart.SaveToFile(AFileName);
    finally
      jpgPart.Free();
    end;
  finally
    { free the desktop and part classes }
    cnvDesktop.Free();
    bmpPart.Free();
  end;
end;

Użycie metody jest bardzo proste:

procedure TMainForm.btnCaptureClick(Sender: TObject);
var
  pntCursor: TPoint;
begin
  { get cursor position on desktop }
  GetCursorPos(pntCursor);
  { capture and save the part of desktop }
  CapturePartOfDesktop(pntCursor, 'C:\Part.jpg');
end;

Źródła aplikacji realizującej powyższy algorytm w załączniku;

Powyższy kod co prawda realizuje pobranie wycinka pulpitu, jednak nie jest zabezpieczony pod kątem pobrania takiego wycinka, który znajduje się zaraz przy krawędzi pulpitu; Jeśli punkt znajdować się będzie w mniejszej odległości niż połowa ustalonego wycinka - jego część pozostanie np. biała (ta która musiałaby zawierać obraz spoza ekranu); W takim przypadku trzeba by przesunąć punkt rysowania krzyżyka w odpowiednie miejsce w wycinku;
Na przyszłość polecam więcej kombinowania, dlatego że tutaj jedynym problemem było skopiowanie współrzędnych do rysowania krzyżyka z mojego wcześniejszego przykładu, w którym oczywiście wpisane były na pałę; Ale ten przykład miał przedstawić prostotę rysowania po kanwie bitmapy stąd sądziłem, że obliczenia już wykonasz sam.

0

@furious programming Ogromne dzięki!

1 użytkowników online, w tym zalogowanych: 0, gości: 1