Foros Club Delphi

Foros Club Delphi (https://www.clubdelphi.com/foros/index.php)
-   C++ Builder (https://www.clubdelphi.com/foros/forumdisplay.php?f=13)
-   -   Guardar y recuperar color pixel originales de una zona pantalla (https://www.clubdelphi.com/foros/showthread.php?t=85218)

NEG1414 16-02-2014 10:34:03

Guardar y recuperar color pixel originales de una zona pantalla
 
Buenas...
Estoy intentando guardar una zona de pantalla, modificarla, y despues recuperarla.. para ello guardo en una matriz los colores de cada pixel que forman la zona ha modificar, de la forma


Código:

TColor Fondo[10][10];

for( x=pX;x<PFinX,x++)
 {
    for (y=pY;y<PFinY,y++)
      {
            Fondo[x][y]= Canvas->Pixels[x][y];
      }
 }

Para recuperar su estado Original:

Código:

for( x=pX;x<PFinX,x++)
 {
    for (y=pY;y<PFinY,y++)
      {
            Canvas->Pixels[x][y]= Fondo[x][y];
      }
 }

Application->ProcessMessages()

Al final no me hace absolutamente nada:(...Donde esta mi error Gracias

ecfisa 16-02-2014 21:10:49

Hola NEG1414.

Usando un arreglo podes hacer:
Código:

...

void copiar(TRect R)
{
  for(int x = R.left; x<R.right; x++)
    for(int y= R.top; y<R.Bottom; y++)
      Fondo[x][y] = Form1->Canvas->Pixels[x][y];
}

void pegar(TRect R, int PX, int PY)
{
  for(int x = R.left; x<R.right; x++)
    for(int y= R.top; y<R.Bottom; y++)
      Form1->Canvas->Pixels[PX+x][PY+y] = Fondo[x][y];
}

void __fastcall TForm1::Button1Click(TObject *Sender)
{
  Canvas->TextOutA(0,0,"PRUEBA");
  // salvar el área de 'PRUEBA'
  copiar(Rect(0, 0, Canvas->TextWidth("PRUEBA"),
    Canvas->TextHeight("PRUEBA")));
  // escribir algo sobre 'PRUEBA'
  Canvas->TextOutA(0 0, "XX");
}

void __fastcall TForm1::Button2Click(TObject *Sender)
{
  // restaurar el área
  pegar(Rect(0,0,Canvas->TextWidth("PRUEBA"),
    Canvas->TextHeight("PRUEBA")), 0, 0);
}

Pero si queres ahorrarte el arreglo bidimensional, podrías guardar el área como un TBitmap, por ejemplo:
Header:
Código:

class TForm1 : public TForm
{
__published:       
  TImage *Image1;
  TLabel *Label1;  // sobre la imágen
  TButton *Button1;
  TButton *Button2;
  ...
  void __fastcall Button1Click(TObject *Sender);
private:       
  Graphics::TBitmap *FBM;
  void __fastcall GetWndArea(int x1, int y1, int x2, int y2);
...

Código:

TForm1 *Form1;

__fastcall TForm1::TForm1(TComponent* Owner)
  : TForm(Owner)
{
  FBM = new Graphics::TBitmap;
  randomize();
}

void __fastcall TForm1::GetWndArea(int x1, int y1, int x2, int y2)
{
  FBM->Width = x2-x1;
  FBM->Height= y2-y1;
  TRect R = Rect(0,0,FBM->Width,FBM->Height);
  FBM->Canvas->CopyRect(R, Canvas, R);
}

void Modificar(Graphics::TBitmap *B)
{
  int x,y;

  for(y=0; y < B->Height; y++) {
    for(x=0; x < B->Width; x++)
      B->Canvas->Pixels[x][y] = (TColor)(rand()% 0xFFFFFFF);
  }
}

void __fastcall TForm1::Button1Click(TObject *Sender)
{
  // salvar el área de la imagen
  GetWndArea(0, 0, Image1->Width,Image1->Height);
  // modificarla
  Modificar(Image1->Picture->Bitmap);
}


void __fastcall TForm1::Button2Click(TObject *Sender)
{
  // restaurar el área
  Canvas->Draw(0, 0, FBM);
}

Saludos :)

NEG1414 17-02-2014 12:59:55

Gracias por por contestame...

En pricipio tomaria la el primer metodo que has indicado para guardar y mostrar una zona de pantalla.. pero me surge un problema... La captura de "trozo" pantalla y posterior "reposicion" se realiza dentro de un componente visual que he creado de la forma:

Código:


  class PACKAGE Grafica : public TCustomControl
  {
        ...........
        ..........

El caso es que en ciertos momentos sobre el componente visualizo un TPanel, este al cumplir su cometido modifica su propiedad Visible a false pero en el componente sigue quedando su imagen...
podria refrescar el componente completamente pero conlleva una serie de operaciones que haria que el refres no fuese fluido y se notara.. para ello prefiero solo "repintar" pixel a pixel la zona donde estaba el panel (que antes habria guardado) de la forma que mostre al inicio del Post...

Al aceder al Repintado desde el componente mediante
Código:

Form1->Canvas->Pixels[PX+x][PY+y] = Fondo[x][y];
no me vale..

(el segundo metodo al incluir un TImage en la operacion me complica el tema considerablemente..)

Puedes indicarme como adaptar el primer metodo a mi caso...Gracias Otra Vez

ecfisa 17-02-2014 19:21:59

Cita:

Empezado por NEG1414 (Mensaje 472701)
(el segundo metodo al incluir un TImage en la operacion me complica el tema considerablemente..)

Hola NEG1414.

Creo que el ejemplo no fué suficientemente claro :(, no es necesario incluir un TImage para capturar el área del formulario.

Tal vez de este modo resulte mas claro:
Código:

__fastcall TForm1::TForm1(TComponent* Owner)
  : TForm(Owner)
{
  FBM = new Graphics::TBitmap;
}

// Guardar área
void __fastcall TForm1::GetWndArea(int x1, int y1, int x2, int y2)
{
  FBM->Width = x2-x1;
  FBM->Height= y2-y1;
  TRect R = Rect(0,0,FBM->Width,FBM->Height);
  FBM->Canvas->CopyRect(R, Canvas, R);
}

// Poner algo en el form, esperarar 1 seg y borrar el área
void __fastcall TForm1::Button1Click(TObject *Sender)
{
  char *t1="TEXTO 1",*t2="TEXTO 2";
 
  Canvas->Ellipse(0, 0, 300, 100);
  Canvas->TextOut(120, 30, t1);
  Canvas->TextOut(120, 30 + Canvas->TextHeight(t1),t2);
  Canvas->Brush->Style = bsClear;
  GetWndArea(0,0,305,105);
  Sleep(1000);
  Canvas->Brush->Color = Color;
  Canvas->Brush->Style = bsSolid;
  Canvas->FillRect(Rect(0,0,FBM->Width,FBM->Height));
}

// restaurar el área borrada
void __fastcall TForm1::Button2Click(TObject *Sender)
{
 Canvas->Draw(0, 0, FBM); 
}

Saludos :)

NEG1414 17-02-2014 19:51:08

Muchas gracias por tu tiempo ecfisa voy a probar y te cuento....

ecfisa 17-02-2014 20:35:56

Hola NEG1414.

Quien dice 30 dice 31... :), yo me refería a implementar algo parecido a esto:
Código:

...
class PACKAGE MiControl : public TCustomControl
{
private:
  Graphics::TBitmap *FArea;
  TControlCanvas *FCtrlCanvas;
  //...
public:
  __fastcall MiControl(TComponent *Owner) : TCustomControl(Owner)
  {
      FArea = new Graphics::TBitmap;
      FCtrlCanvas = new TControlCanvas;
      FCtrlCanvas->Control = this;
  };
  __fastcall ~MiControl()
  {
      delete FArea;
      delete FCtrlCanvas;
  }
  void __fastcall LoadArea(void)
  {
      FArea->Width  = Left + Width;
      FArea->Height = Top + Height;
      TRect R = Rect(0, 0, FArea->Width, FArea->Height);
      FArea->Canvas->CopyRect(R, Canvas, R);
    };
  __property TControlCanvas *Canvas = { read = FCtrlCanvas, write = FCtrlCanvas};
  __property Graphics::TBitmap *Area = { read = FArea, write = FArea };
};

MiControl *mc;

void __fastcall TForm1::FormCreate(TObject *Sender)
{
  mc = new MiControl(this);
}

// Crear y mostrar
void __fastcall TForm1::btnCreateClick(TObject *Sender)
{
  mc->Left  = 0;
  mc->Top    = 0;
  mc->Width  = 250;
  mc->Height = 300;
  mc->Parent = this;
  // Dibujar algo
  mc->Canvas->Ellipse(0,0,mc->Width-10,mc->Height-40);
  // Guardar área
  mc->LoadArea();
}
// borrar área
void __fastcall TForm1::btnClearClick(TObject *Sender)
{
  mc->Canvas->Brush->Color = Color;
  mc->Canvas->FillRect(Rect(0,0,mc->Width,mc->Height));
}

// Restaurar área
void __fastcall TForm1::btnRestoreClick(TObject *Sender)
{
  mc->Canvas->Draw(0,0,mc->Area);
}

void __fastcall TForm1::FormDestroy(TObject *Sender)
{
  delete mc;
}

Saludos :)

NEG1414 18-02-2014 12:19:57

Buenas..

Siento ser tan pesado pero no doy hecho que se reponga la imagen del trozo de componente que deseo... la verdad es que dibuja el area totalmente blanca...

Dado que la todas las operaciones las hago desde el propio componente (por si sirve de algo lo que se va ha visiualizar en el componente lo dibujo mediante el metodo paint() usandola la propiedad canvas) lo he dadptado asi:

Código:


 
class PACKAGE TGrafica : public TCustomControl
{
  __published: // IDE-managed Components 
 
  TPanel *PanelInf;
  Graphics::TBitmap *AreaPanelInf;
  TControlCanvas    *FCtrlCanvas;
 
  int PPx; //Posicion X esquina superior Derecha Panel Informacion
  int PPy; //Posicion Y esquina superior Derecha Panel Informacion
 
    .................
 
 
void TGrafica::MostrarPanelDatos()
{
 
  AreaPanelInf->Width  = 16;
  AreaPanelInf->Height  = 10;
 
  TRect R = Rect(PPx,PPy,AreaPanelInf->Width,AreaPanelInf->Height);
  AreaPanelInf->Canvas->CopyRect(R, Canvas, R);
 
  //Creo y visualizo Panel Informacion
  PanelInf = new TPanel (this);
  PanelInf->Parent  =    this;
  PanelInf->Height  = 10;
  PanelInf->Width  = 16;
  PanelInf->Left = PPx;
  PanelInf->Top  = PPy;
  PanelInf->Visible = true;
 
  Application->ProcessMessages();
 
}
 
 
//---------------------------------------------------------------------------
//Restaura que ocupada el Panel Informativo
 
void TGrafica::RestaurAreaPanel()
{
 
  PanelInf->Visible = false;
 
  this->Canvas->Draw(PPx,PPy,AreaPanelInf);
 
  Application->ProcessMessages();
}

Alguna idea... Gracias

NEG1414 25-02-2014 12:04:36

Buenas..

Retomo el tema adjuntando una pequeña aplicacion que intenta explicar graficamente mi problema...


https://app.box.com/s/6gvqol66wahyhuk1negu


He llegado a la conclusion de que uasando el codigo siguiente (gracias ecfisa):

Código:


      FArea->Width  = Left + Width;
      FArea->Height = Top + Height;
      TRect R = Rect(0, 0, FArea->Width, FArea->Height);
      FArea->Canvas->CopyRect(R, Canvas, R);

Solo guarda el area correspondiente dibujada mediante el procedimiento paint() del componente.. ignorado el area "redibujada" a posteriori desde otro procedimiento (que es mi caso)...
Dado que solo modifica areas afectadas por el evento paint().. no gano nada guardando y recuperando una pequeña area ya que el Procedimiento paint() regenera la imagen de todo el componente si o si..

Hay alguna manera de regenerar el area dibujada desde otro procedimiento diferente al Paint()

Espero haberme explicado bien... Gracias.

ecfisa 26-02-2014 01:52:06

Hola NEG1414.

Descargué tu adjunto para poder hacerme una mejor idea del problema pero no pude acceder. No encuentra la clase TPrueba, falta Prueba.h y me pide el package lmd70se_B6.bpi (que no poseo).

En cuanto al ejecutable me genera el error:
Cita:

No se encuentra el punto de entrada del procedimiento
@@Gifviewer@finalize en la biblioteca de vinculos dinámicos
...
Saludos :)

escafandra 26-02-2014 01:59:08

Hola NEG1414. No puedo ejecutar ni compilar tu ejemplo pues faltan los archivos correspondientes a TPrueba.

Según entiendo tu sólo quieres repintar un área determinada tras hacer desaparecer una imagen que la ocupaba. Para ello tienes la API InvalidateRect siempre que tu control sea una ventana con Handle (hWnd):

Código:


  TRect cr; // aqui colocas el valor del rectángulo a repintar
  InvalidateRect(hWnd, &cr, true);
  SendMessage(hWnd, WM_NCPAINT, 0, 0);

Saludos.

PD mientras escribía ecfisa encontraba el mismo error que yo en tu ejemplo... :)

NEG1414 26-02-2014 12:12:22

Buenas..

Primero daros las gracias y pediros perdon por no haber incluido el componente Prueba..
Acabo de Actualizar el archivo con el componente.. lo podeis descargar de la misma direcion

https://app.box.com/s/6gvqol66wahyhuk1negu

Gracias o tra vez.

Escifa: puedes crear una nueva aplicacion con los mismos archivos descargados y ya no te pedira el componente creapackage lmd70se_B6.bpi ...

escafandra 27-02-2014 15:38:52

Hola NEG1414. Pude compilar tu proyecto y he visto que tienes un error al capturar el AreaPanel. El erreo está al definir el TRect fuente y destino. Lo correcto sería así:
Código:

void TPrueba::MostrarPanel()
{

  AreaPanel->Width  = 20;
  AreaPanel->Height  = 20;

  TRect Rs = Rect(PPx, PPy, PPx + AreaPanel->Width, PPy + AreaPanel->Height);
  TRect Rd = Rect(0,0,AreaPanel->Width,AreaPanel->Height);
  AreaPanel->Canvas->CopyRect(Rd, Canvas, Rs);

  //Posicionar  y visualizar
  Panel->Left    = PPx;
  Panel->Top    = PPy;
  Panel->Visible = true;

  Application->ProcessMessages();
}

Si salvas en disco AreaPanel, te darás cuenta que si estaba amarillo, te guarda un cuadradito amarillo...

Pero con esto no resuelves el problema pues al poner invisible tu panel automáticamente se llama al evento paint que redibuja tu control sin saber que había una zona amarilla. Como el evento paint lo dispara WM_PAINT, va a ocurrir despues de que tu redibujes la zona en cuestión y te la machaca.

Sólo tienes una forma de evitarlo que es usar un TImage o mejor que tu control de derive de un TImage, en lugar de un TWinControl, En ese caso lo que pintes en el canvas quedará "memorizado" para el evento Paint y evitas efectos indeseables durante el pintado pues no. Tu pintado de rayas iniciales no debe ir en el evento pain sino en funcion a parte que puedes llamar pintafondo y que ejecutas solo al crear el componente.

Saludos.

NEG1414 28-02-2014 09:35:15

Muchas gracias por contestarme scafandra... He entendido perfectamente tu explicacion y me resigno...

NEG1414 28-02-2014 10:29:16

Soy un poco cabezon y aun me surge una duda el procedimiento paint(); creo que redibuja siempre en segundo plano( la zona rayada debajo de la zona amarilla) si esto es asi y el proceso es:

Primero zona rayada (Paint() segundo plano)
Activo Repintar : zona amarilla (Primer plano)
Guardo area Panel (¿¿¿amarilla??)
Visualizo Panel (Paint() zona Rayada segundo plano)
Oculto Panel(Paint() zona rayada segundo plano)
Repinto Arepanel

Al repintar el area del panel en teoria amarilla en primer plano por que repinta rayado (o puede se incluso transparente)

escafandra 28-02-2014 19:31:45

El problema es que tú no puedes controlar cuando el sistema va a hacer efectivo el Paint puesto que funciona con un mensaje en la cola y se ejecutará cuando le toque, incluso otra APP que se te ponga encima va a provocar el repintado y tu zona amarilla desaparecerá. Esto hace que ocurran efectos aparentemente caprichosos.

Sólo puede funcionar si es la respuesta a WM_PAINT la que te repinta la zona amarilla. Cuando el evento Paint repinta un canvas que tiene guardado, entonces lo hará correctamente, eso es lo que hace un TImage (Tiene un TBitmap que repintará en el canvas). Puedes realizar esto, heredar tu componente de un TImage o que tu componente lo contenga. Las últimas opciones son más sencillas.

Saludos.

NEG1414 01-03-2014 22:11:07

Muchas gracias escafandra ahora lo entiendo.

NEG1414 05-03-2014 10:45:25

Buenas...

Siento ser un pesado.. pero mis limites no me permiten prescindir de vuestra ayuda...
He decidido seguir los consejos de escafandra y modificar el componente (Tprueba) para que herede de un TImage... pero me surge un problema, al inicializar el Tpanel me salta el error :

Cannot convert TPrueba* const´to TWinControl*

En la linea
Código:


Panel->Parent  = this;

Supongo que al "convertir" el componente (TPrueba) en un componente "TImage" no puede heredar nada de el un componente TWincontrol como TPanel....

Tiene alguna Solucion o solo me queda incluir un Timage en el componente TPrueba original que hereda de TCustomControl (me complicaria un poco mi aplicacion)...

Espero haberme explicado bien... Gracias

escafandra 06-03-2014 00:03:50

En el caso de que necesites usar un componente cuyo Parent sea tu control, éste debe ser un TWinControl. Para conseguirlo tienes derivar de TWinControl y sobre éste colocas un TImage alineado a alClient. Al parent de tu TPanel la asignas tu control.


Saludos.

NEG1414 06-03-2014 17:16:47

Buenas...

Definitivamente desisto, lo dejo... he hecho heredar TPrueba1 de un TWincontrol... he colocado un TImage que sera donde se muestre el dibujo que hago mediante Canvas...

Cita:

__fastcall TPrueba::TPrueba(TComponent* Owner)
: TWinControll(Owner)
{
Panel = new TPanel(this);
Panel->Parent = this;

Imagen = new TImage(this)
Imagen->Parent = this...
.......................
....................
Todo correcto...pero a la hora de mostrar el Tpanel...este aparece en segundo Plano Debajo del Timage (no se ve)

gracias por vuestro tiempo.

escafandra 06-03-2014 23:07:43

No desistas, hombre...

Mira este ejemplo para poder empezar y seguir:
Código:

class TPrueba: public TWinControl
{
  private:
  TImage *Imagen;
  TPanel *Panel;
  public:
  __fastcall TPrueba(TComponent* Owner);
};

Código:

__fastcall TForm1::TForm1(TComponent* Owner)
        : TForm(Owner)
{
  TPrueba *Prueba = new TPrueba(this);
  Prueba->Width = 200;
  Prueba->Height = 200;
  Prueba->Parent = this;
}
//---------------------------------------------------------------------------


__fastcall TPrueba::TPrueba(TComponent* Owner): TWinControl(Owner)
{
  Imagen = new TImage(this);
  Imagen->Parent = this;
  Imagen->Align = alClient;
  for(int y=0; y<200; y++) {
    if((y%2)==0){Imagen->Canvas->Pen->Color = clRed;}else{Imagen->Canvas->Pen->Color = clBlue;}
    Imagen->Canvas->Pen->Width = 1;
    Imagen->Canvas->MoveTo(0,y);
    Imagen->Canvas->LineTo(199,y);
  }

  Panel = new TPanel(this);
  Panel->Top = 0;
  Panel->Left = 0;
  Panel->Width = 20;
  Panel->Height = 20;
  Panel->Parent = this;
}

Saludos.


La franja horaria es GMT +2. Ahora son las 07:51:11.

Powered by vBulletin® Version 3.6.8
Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
Traducción al castellano por el equipo de moderadores del Club Delphi