kolorowanka delphi 7

0

Próbuję od kilku dni napisać prostą kolorowankę w Delphi 7.
Znalazłam trochę, ale nie daję rady z wypełnieniem.

 unit koloro;

interface
  {$R RESOURCE.RES RESOURCE.RC}
uses
  Jpeg, Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, ExtDlgs, StdCtrls, ExtCtrls, ColorGrd, Grids, ComCtrls,
  Menus, Clipbrd, Buttons, ToolWin;

type
    TForm1 = class(TForm)
    Button4: TButton; //zamyka
    Button8: TButton; //kaczor
    Button9: TButton; //kubuspro
    Button10: TButton;//kubus
    Button11: TButton; //pluto
    Button13: TButton;  //tomjerry
    Button2: TButton; //zapisz
    Button3: TButton; //drukuj
    Button14: TButton; //ust druku
    Button7: TButton; //kolor
    SavePictureDialog1: TSavePictureDialog;
    PrinterSetupDialog1: TPrinterSetupDialog;
    ColorDialog1: TColorDialog;
    PrintDialog1: TPrintDialog;
    GroupBox1: TGroupBox;
    GroupBox2: TGroupBox;
    procedure Button4Click(Sender: TObject);
    procedure Button3Click(Sender: TObject);
    procedure Button8Click(Sender: TObject);
    procedure Button10Click(Sender: TObject);
    procedure Button9Click(Sender: TObject);
    procedure Button11Click(Sender: TObject);
    procedure Button13Click(Sender: TObject);
    procedure Button7Click(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure FormPaint(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure Button14Click(Sender: TObject);
    procedure FormMouseDown(Sender: TObject; Button: TMouseButton;
      Shift: TShiftState; X, Y: Integer);
end;
var
  Form1: TForm1;
  bm: TBitmap;
  CurrentColor: TColor = clBlack;


implementation
{$R *.dfm}

{
type
  TASPixmap = array of packed array of TRGBQuad;

  TRGB32Array = packed array[0..MaxInt div SizeOf(TRGBQuad)-1] of TRGBQuad;
  PRGB32Array = ^TRGB32Array;

  TScanline = TRGB32Array;
  PScanline = ^TScanline;

function IsIntInInterval(x, xmin, xmax: integer): boolean; 
begin
  IsIntInInterval := (xmin <= x) and (x <= xmax);
end;

function PascalColorToRGBQuad(const Color: TColor): TRGBQuad;
begin
  with Result do
  begin
    rgbBlue := GetBValue(Color);
    rgbGreen := GetGValue(Color);
    rgbRed := GetRValue(Color);
    rgbReserved := 0;
  end;
end;

function RGBQuadEqual(const Color1: TRGBQuad; const Color2: TRGBQuad): boolean;
begin
  RGBQuadEqual := (Color1.rgbBlue = Color2.rgbBlue) and
                  (Color1.rgbGreen = Color2.rgbGreen) and
                  (Color1.rgbRed = Color2.rgbRed);
end;

function PMFloodFill(Pixmap: TASPixmap; const X0: integer; const Y0: integer; const Color: TColor): TASPixmap;
var
  w, h: integer;
  MatchColor, QColor: TRGBQuad;
  Queue: packed array of TPoint;
  cp: TPoint;

  procedure push(Point: TPoint);
  begin
    SetLength(Queue, length(Queue) + 1);
    Queue[High(Queue)] := Point;
  end;

  function pop: TPoint;
  var
    lm1,w: integer;
  begin
    assert(length(Queue) > 0);
    result := Queue[0];
    lm1 := length(Queue) - 1;
    if lm1 > 0 then
      MoveMemory(@(Queue[0]), @(Queue[1]), lm1 * sizeof(TPoint));
    SetLength(Queue, lm1);
  end;
begin
  h := length(Pixmap);
  if h > 0 then
    w := length(Pixmap[0]);
  result := Pixmap;
  if not (IsIntInInterval(X0, 0, w-1) and IsIntInInterval(Y0, 0, h-1)) then Exit;
  MatchColor := Pixmap[Y0, X0];
  QColor := PascalColorToRGBQuad(Color);
  SetLength(Queue, 0);
  push(point(X0, Y0));
  while length(Queue) > 0 do
  begin
    if RGBQuadEqual(result[Queue[0].Y, Queue[0].X], MatchColor) then
      result[Queue[0].Y, Queue[0].X] := QColor;

    cp := pop;

    if cp.X > 0 then
      if RGBQuadEqual(result[cp.Y, cp.X - 1], MatchColor) then
      begin
        result[cp.Y, cp.X - 1] := QColor;
        push(point(cp.X - 1, cp.Y));
      end;

    if cp.X < w-1 then
      if RGBQuadEqual(result[cp.Y, cp.X + 1], MatchColor) then
      begin
        result[cp.Y, cp.X + 1] := QColor;
        push(point(cp.X + 1, cp.Y));
      end;

    if cp.Y > 0 then
      if RGBQuadEqual(result[cp.Y - 1, cp.X], MatchColor) then
      begin
        result[cp.Y - 1, cp.X] := QColor;
        push(point(cp.X, cp.Y - 1));
      end;

    if cp.Y < h-1 then
      if RGBQuadEqual(result[cp.Y + 1, cp.X], MatchColor) then
      begin
        result[cp.Y + 1, cp.X] := QColor;
        push(point(cp.X, cp.Y + 1));
      end;
  end;
end;

function GDIBitmapToASPixmap(const Bitmap: TBitmap): TASPixmap;
var
  scanline: PScanline;
  width, height, bytewidth: integer;
  y: Integer;
begin

  Bitmap.PixelFormat := pf32bit;

  width := Bitmap.Width;
  height := Bitmap.Height;
  bytewidth := width * 4;

  SetLength(Result, height);
  for y := 0 to height - 1 do
  begin
    SetLength(Result[y], width);
    scanline := @(Result[y][0]);
    CopyMemory(scanline, Bitmap.ScanLine[y], bytewidth);
  end;

end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  bm := TBitmap.Create;
end;

procedure GDIBitmapAssign(Bitmap: TBitmap; const Pixmap: TASPixmap);
var
  y: Integer;
  scanline: PScanline;
  bytewidth: integer;
begin
  Bitmap.PixelFormat := pf32bit;
  bytewidth := Bitmap.Width * 4;

  for y := 0 to Bitmap.Height - 1 do
  begin
    scanline := @(Pixmap[y][0]);
    CopyMemory(Bitmap.ScanLine[y], scanline, bytewidth);
  end;
end;

procedure TForm1.FormPaint(Sender: TObject);
var
bmp:TBitmap;
begin
  Canvas.Draw(0, bmp.Height, bm);
end;
procedure TForm1.Button4Click(Sender: TObject);
begin
    close;
end;
}
procedure TForm1.Button3Click(Sender: TObject);  //drukuj
begin
    PrintDialog1.Execute;
end;


procedure TForm1.Button8Click(Sender: TObject);          
var
  Bitmap : TBitmap;
begin
with form1 do with canvas do
  begin
   brush.Color := color;
   fillrect(rect(0,0,width,height));
  end;
  Bitmap := TBitmap.Create;
  Bitmap.LoadFromResourceName(hInstance, 'kaczor');
  Canvas.Draw(0, 0, Bitmap);
  Bitmap.Free;
end;
procedure TForm1.Button10Click(Sender: TObject); 
var
  Bitmap : TBitmap;
begin
with form1 do with canvas do
  begin
   brush.Color := color;
   fillrect(rect(0,0,width,height));
  end;
  Bitmap := TBitmap.Create;
  Bitmap.LoadFromResourceName(hInstance, 'kubus');
  Canvas.Draw(0, 0, Bitmap);
  Bitmap.Free;
end;
procedure TForm1.Button9Click(Sender: TObject);
var
  Bitmap : TBitmap;
begin
with form1 do with canvas do
  begin
   brush.Color := color;
   fillrect(rect(0,0,width,height));
  end;
  Bitmap := TBitmap.Create;
  Bitmap.LoadFromResourceName(hInstance, 'kubuspro');
  Canvas.Draw(0, 0, Bitmap);
  Bitmap.Free;
end;

procedure TForm1.Button11Click(Sender: TObject);  
var
  Bitmap : TBitmap;
begin
with form1 do with canvas do
  begin
   brush.Color := color;
   fillrect(rect(0,0,width,height));
  end;
  Bitmap := TBitmap.Create;
  Bitmap.LoadFromResourceName(hInstance, 'pluto');
  Canvas.Draw(0, 0, Bitmap);
  Bitmap.Free;
end;

procedure TForm1.Button13Click(Sender: TObject);       
var
  Bitmap : TBitmap;
begin
 with form1 do with canvas do   
  begin
   brush.Color := color;
   fillrect(rect(0,0,width,height));
  end;
  Bitmap := TBitmap.Create;
  Bitmap.LoadFromResourceName(hInstance, 'tj');
  Canvas.Draw(0, 0, Bitmap);
  Bitmap.Free;
end;
procedure TForm1.Button7Click(Sender: TObject);         
begin
  with TColorDialog.Create(self) do
    try
      Color := CurrentColor;
      Options := [cdFullOpen];
      if Execute then
        CurrentColor := Color;
    finally
      Free;
    end;
end;

procedure TForm1.Button2Click(Sender: TObject);             
begin
      SavePictureDialog1.Execute;
end;

procedure TForm1.Button14Click(Sender: TObject);              
begin
    PrinterSetupDialog1.Execute;
end;

procedure TForm1.FormMouseDown(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
var
  x0, y0: integer;
  pm: TASPixmap;
begin
  x0 := X;
  y0 := Y - GroupBox1.Height;

  if IsIntInInterval(x0, 0, GroupBox1.Width) and IsIntInInterval(y0, 0, GroupBox1.Height) then
  begin
    pm := GDIBitmapToASPixmap(bm);
    pm := PMFloodFill(pm, x0, y0, CurrentColor);
    GDIBitmapAssign(bm, pm);
  end;
end;

end.

To co jest w komentarzu ( {} ) tego nie pojmuję i na 99,9% tam jest błąd.
Obrazki wczytuję z resource'a, działa.
Po uruchomieniu pokazuje się wybór obrazków, po wciśnięci odpowiedniego przycisku pokazuje się dany obrazek, ale po wybraniu koloru dany obszar nie wypełnia się.

Tu pytanie:

  1. Czy da się prościej napisać wypełnienie danych kształtów?
  2. Dlaczego po wybraniu koloru i kliknięciu na obrazek nie wypełnia się?

PS: Bitmapę wczytuję na formularzu, bo w image1 i paintbox1 wywalało mi ciągle błędy.

0

Canvas udostepnia Ci funkcje odpoiedzialne za wypelnianie danym kolorem. Czemu z nich nie skorzystasz?

0

Musiałabym robić to w paintbox'ie chyba, prawdę mówiąc zaczynam dopiero Delphi (4 TI)
I nie wiem jak na Bmp czy Jpegu ograniczyć miejsce wypełnienia. Chociaż próbowałam używać Canvasa to zupełnie nic z tego nie wyszło..

0

Obrazek do kolorowania na przykład taki:

user image

1
iwanthim napisał(a)

I nie wiem jak na Bmp czy Jpegu ograniczyć miejsce wypełnienia. Chociaż próbowałam używać Canvasa to zupełnie nic z tego nie wyszło..

Z reguły jeżeli wypełniasz jakiś obszar, to granicą obszaru są piksele o innym kolorze; Choćby kolor był inny tylko o 1, to już jego nie piwinien algorytm wypełniający pomalować; Stąd jeżeli znajdziesz sposób na wypełnianie danym kolorem w miejscu o dokładnych współrzędnych, grafika w formacie JPEG nie zostanie prawidłowo wypełniona, gdyż algorytm konwertujący grafikę do tego formatu kompresuje zawartość i tworzy bloki o jednym kolorze, więc tło zawsze jest nieregularne; Gołym okiem tego nie widać, ale w powiększeniu owszem; Stosuj grafikę, która nie wspiera konwersji stratnej, taka jak BMP czy PNG, jednak dla tej drugiej opracowanie algorytmu wypełniania będzie trudniejsze;

1
 image1.Canvas.Brush.Color:=clRed;
 image1.Canvas.FloodFill(x,y, clWhite, fsSurface);

w tym przypadku clRed to kolor ktorym zastąpisz clWhite.
wiec clWhite powinien byc kolorem pobieranym spod kursora (kolor:=canvas.pixels[x,y];)
x y to oczywiscie pozycja kursora na obrazku.

0

czy zamiast clRed mogę dać ColorDialog1.Color żeby odczytało ten, który zostanie wybrany?

0

A czy między TColor a TColor jest jakaś różnica..?

0

Nie. Zadaję takie pytania bo chcę wiedzieć. Jak pisałam, dopiero zaczynam pracę z Deplhi. Na zajęciach go nawet nie mam, tylko staram się w miarę samodzielnie..

Dziękuję wszystkim za dotychczasową pomoc

0

Jeszcze pytanie: czy

 image1.canvas... 

mam dawać jako reakcję na image1, czy źle myślę?

0
iwanthim napisał(a)

mam dawać jako reakcję na image1, czy źle myślę?

Możesz jaśniej...? Co znaczy reakcję w Twoim rozumieniu?

0

Reakcja- zdarzenie na dany bodziec, w tym przypadku kliknięcie w danym miejscu. Chodzi mi o to, że muszę przypisać FloodFill'a do Image.

Z tym już nie mam problemu, ale chyba muszę odczytać pozycję myszki x,y..

procedure TForm1.Image1Click(Sender: TObject);
var
  x,y:integer;
begin
    image1.canvas.Brush.Style := bsSolid;
    image1.Canvas.Brush.Color := ColorDialog1.Color;
    image1.Canvas.FloodFill(x,y, clWhite, fsSurface );
end; 

Jak w miejsce x i y wstawiłam 1,1 to wypełniło mi tło, ale kolorem granatowym i nie w miejscu gdzie kliknęłam, ale to pewnie dlatego, że określiłam współrzędne.. I stąd pytanie czy muszę w takim przypadku określić położenie wskaźnika myszy?

1
iwanthim napisał(a)

Jak w miejsce x i y wstawiłam 1,1 to wypełniło mi tło, ale kolorem granatowym i nie w miejscu gdzie kliknęłam, ale to pewnie dlatego, że określiłam współrzędne.. I stąd pytanie czy muszę w takim przypadku określić położenie wskaźnika myszy?

Możesz to zrobić dwojako:

  1. ręcznie pobrać położenie kursora
  2. skorzystać ze zdarzenia OnMouseDown komponentu
    Wykorzystaj, którą chcesz;

Jeśli chcesz ręcznie pobrać pozycję kursora - skorzystaj z funkcji GetCursorPos:

procedure GetCursorPos(var P: TPoint);

gdzie jako argument podajesz zmienną typu TPoint;


Jeśli chodzi o zdarzenie OnMouseDown:

procedure TForm1.Image1MouseDown(Sender: TObject; Button: TMouseButton;
                                 Shift: TShiftState; X, Y: Integer);

W argumentach X i Y masz podaną aktualną pozycję kursora na ekranie;


Jeśli chodzi o kolor, @cimak napisał Ci jak posługiwać się metodą FloodFill:

Image1.Canvas.Brush.Color := clRed;
Image1.Canvas.FloodFill(X, Y, clWhite, fsSurface);

w tym przypadku clRed to kolor ktorym zastąpisz clWhite.
wiec clWhite powinien byc kolorem pobieranym spod kursora (Kolor := Image1.Canvas.Pixels[X, Y];)
x y to oczywiscie pozycja kursora na obrazku.

Przykładowy kod:

procedure TForm1.Image1MouseDown(Sender: TObject; Button: TMouseButton;
                                 Shift: TShiftState; X, Y: Integer);
var
  dlgColor: TColorDialog;
begin
  dlgColor := TColorDialog.Create(Self);

  try
    with dlgColor do
      begin
        Options := [cdFullOpen];

        if Execute() then
          begin
            Image1.Canvas.Brush.Color := dlgColor.Color;
            Image1.Canvas.FloodFill(X, Y, Image1.Canvas.Pixels[X, Y], fsSurface);
          end;
      end;
  finally
    FreeAndNil(dlgColor);
  end;
end;

Powinien działać;

0

Dzisiaj miałam programowanie i właśnie nauczyciel mi to podpowiedział :) ale niestety program się później zwiesił, a nie zapisałam, bo myślałam, że mi się przy kompilowaniu zapisze no i utraciłam to co podpowiedział.

Dziękuję, że to wstawiłeś, dzięki Tobie odzyskałam :)

i mam pytanie. W image daję stretch na true, żeby obrazek się dopasował, ale wtedy nie koloruje tam, gdzie kliknę. Wiecie jak to ogarnąć?
Nauczyciel pokazał mi przy wczytywaniu obrazka odczytanie współrzędnych bitmapy, tak że czyta jak myszką ruszam po bitmapie a nie po image i potem podmieniał te wartości
przykładowo: zmienne

ax, ay:real;

potem odczytał szerokość i wysokość bitmapy i podstawił pod zmienne ax, ay, a następnie zrobił coś w tym stylu:

 [Round(ax/x),Round(ay/y)]; //przy pixel
1
iwanthim napisał(a)

Nauczyciel pokazał mi przy wczytywaniu obrazka odczytanie współrzędnych bitmapy, tak że czyta jak myszką ruszam po bitmapie a nie po image i potem podmieniał te wartości

Nie rozumiem o czym piszesz...

iwanthim napisał(a)

ruszam po bitmapie a nie po image

Co to znaczy po bitmapie? Jeśli masz komponent z klasy TImage, możesz przecież przechwycić współrzędne kursora myszy w kilku zdarzeniach:

procedure TForm1.Image1MouseUp(Sender: TObject; Button: TMouseButton;
                               Shift: TShiftState; X, Y: Integer);

procedure TForm1.Image1MouseDown(Sender: TObject; Button: TMouseButton;
                                 Shift: TShiftState; X, Y: Integer);

procedure TForm1.Image1MouseMove(Sender: TObject; Shift: TShiftState; X,
                                 Y: Integer);

więc także podczas przesuwania kursora po komponencie;

Jeżeli chodzi o właściwość Stretch, nauczyciel pokazał Ci w jaki sposób pobrać współrzędne kursora w momencie kliknięcia, po czym przetłumaczyć je na grafikę o normalnych rozmiarach (którą komponent przechowuje we właściwościach Width i Height) i obsłużyć;

Sprawa jest prosta, jeśli masz grafikę o rozmiarach 100x100px, a komponent ma rozmiar 200x200px (czyli jest dwa razywiększy) oraz właściwość Stretch na True, to jeżeli klikniesz w punkcie o współrzędnych [100, 100], to tak, jak być kliknął w punkt [50, 50] na tej zmniejszej grafice; Trzeba po prostu znaleźć kliknięty punkt na mniejszej grafice; Dlatego posłużył się typem zmiennoprzecinkowym; Po obliczeniu współrzędnych punku na mniejszej grafice ich wartości trzeba zaokrąglić (patrz: funkcja Round), ponieważ współrzędne to liczby całkowite;

Ot cała historyja :-P


iwanthim napisał(a)

ale niestety program się później zwiesił, a nie zapisałam, bo myślałam, że mi się przy kompilowaniu zapisze no i utraciłam to co podpowiedział.

Moim nawykiem z nauki pascala (na TP7) było każdorazowe zapisywanie projektu przed (kompilacją i) uruchomieniem, ponieważ jak program się zawiesił i trzeba było go awaryjnie zamknąć - wyłączał się razem z konsolą i traciłem nowe niezapisane dane; Dobry nawyk, nigdy nie tracę nowo napisanego kodu; Polecam zawsze przed kompilacją wciśnąć kombinacje Shift + Ctrl + S - zapisane zostaną wszystkie otwarte i zmodyfikowane pliki;

0
Furious Programming napisał(a)

Jeżeli chodzi o właściwość Stretch, nauczyciel pokazał Ci w jaki sposób pobrać współrzędne kursora w momencie kliknięcia, po czym przetłumaczyć je na grafikę o normalnych rozmiarach (którą komponent przechowuje we właściwościach Width i Height) i obsłużyć;

rozmiary "normalnej" grafiki, tj nierozciągniętej, komponent nie trzyma w width i height (w sensie image1.width).
tam sa trzymane "rozciągnięte" wymiary.
"normalne" bedą -dla bitmapy- w image1.picture.bitmap.width;

0
cimak napisał(a)

rozmiary "normalnej" grafiki, tj nierozciągniętej, komponent nie trzyma w width i height (w sensie image1.width).
tam sa trzymane "rozciągnięte" wymiary.
"normalne" bedą -dla bitmapy- w image1.picture.bitmap.width;

Dokładnie; Kolega pisał wcześniej właśnie o Bitmap, więc napisałem same identyfikatory właściwości; Mój błąd, mogłem napisać pełną ścieżkę;

W każdym razie chodzi właśnie o te dwie właściwości:

Image.Picture.Bitmap.Width;
Image.Picture.Bitmap.Height;

Dzięki za sprostowanie @cimak;

0
Furious Programming napisał(a)

W każdym razie chodzi właśnie o te dwie właściwości:

Image.Picture.Bitmap.Width;
Image.Picture.Bitmap.Height;

Dzięki za sprostowanie @cimak;

to wiem właśnie, tylko dać to przy wczytywaniu bitmapki, czy przy kolorowaniu?
Przy kolorowaniu mam to:

image1.Picture.Bitmap.Canvas.FloodFill(x,y, Image1.Picture.Bitmap.Canvas.Pixels[Round(ax/x), Round(ay/y)], fsSurface );
1
procedure TForm1.Image1MouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
  { najpierw zamien wspolrzedne ekranowe na wspolrzedne piksela bitmapy }
  X := X * Image1.Picture.Bitmap.Width / Image1.Width;
  Y := Y * Image1.Picture.Bitmap.Height / Image1.Height;

  { pozniej malujemy }
  Image1.Picture.Bitmap.Canvas.FloodFill(X, Y, Image1.Picture.Bitmap.Canvas.Pixels[X, Y], fsSurface);
end;
1

@adf88, nie można w ten sposób dzielić liczb całkowitych... Trzeba skorzystać z operatora div:

X := X * Image1.Picture.Bitmap.Width div Image1.Width;
Y := Y * Image1.Picture.Bitmap.Height div Image1.Height;

lub funkcji MulDiv():

X := MulDiv(X, Image1.Picture.Bitmap.Width, Image1.Width);
Y := MulDiv(Y, Image1.Picture.Bitmap.Height, Image1.Height);
0

Dziękuję kochani, wszystko działa! :*

pozdrawiam, Agnieszka

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