![]() |
Efectos con Scanline
Tbitmap.scanline es una propiedad indexada de solo lectura que devuelve un puntero a una fila de pixeles de un bitmap. Es la forma más rápida de acceder a los píxeles de una imagen y esto depende del formato del mapa de bits que se indica desde la propiedad Pixelformat de la unit Graphics. TPixelFormat = (pfDevice, pf1bit, pf4bit, pf8bit, pf15bit, pf16bit, pf24bit, pf32bit, pfCustom); Ejemplo de carga un bitmap: VAR bmp: tbitmap; BEGIN bmp := tbitmap.Create; TRY IF OpenDialog1.Execute THEN BEGIN bmp.LoadFromFile(OpenDialog1.FileName); bmp.PixelFormat := pf24bit; Invalidate; { Mostrar la imagen } END; FINALLY bmp.Free; END; END; Cuando creamos un mapa de bits cada pixel se inicializa al máximo valor, es decir a 255 por eso el mapa de bits es blanco, ese el formato de pixel predeterminado. Un pixel en un mapa de bits de 24 bits se describe con 3 valores rojo, verde y azul, que se almacenan en la memoria en orden inverso: azul, verde y rojo. El puntero a la matriz de bytes que hace scanline se vería de la siguiente forma: https://www.delphipraxis.net/data:im...AAAElFTkSuQmCC Para obtener el resultado anterior hay que asignar el resultado de Scanline a un puntero de bytes: pByteArray VAR p: PByteArray; BEGIN p := FImage.ScanLine[0]; END; Por ejemplo para poner el primer pixel de de la primera fila de la imagen de color negro y el segundo de color blanco habría que hacer: VAR p: PByteArray; BEGIN p := FImage.ScanLine[0]; { lee la primera fila } p[0] := 0; { primer pixel de color negro } p[1] := 0; p[2] := 0; p[3] := 255; { segundo pixel de color blanco } p[4] := 255; p[5] := 255; Invalidate; END; También podemos usar la siguiente estructura: type TRGBTriple = packed record rgbtBlue: Byte; rgbtGreen: Byte; rgbtRed: Byte; end; Para conseguir que la segunda fila del bitmap sea de color negro: type PRGBTripleArray = ^TRGBTripleArray; TRGBTripleArray = array[0..4095] of TRGBTriple; procedure TForm1.Button1Click(Sender: TObject); var I: Integer; Bitmap: TBitmap; Pixels: PRGBTripleArray; begin Bitmap := TBitmap.Create; try Bitmap.Width := 3; Bitmap.Height := 2; Bitmap.PixelFormat := pf24bit; // get pointer to the second row's raw data Pixels := Bitmap.ScanLine[1]; // iterate our row pixel data array in a whole width for I := 0 to Bitmap.Width - 1 do begin Pixels[I].rgbtBlue := 0; Pixels[I].rgbtGreen := 0; Pixels[I].rgbtRed := 0; end; Bitmap.SaveToFile('c:\Image.bmp'); finally Bitmap.Free; end; end; De lo anterior podemos observar que el primer pixel del bitmap se encuentra en el offset 0 de la primera fila, el segundo en el offset 3, el tercero en el offset 6 esto quiere decir que el byte en la ubicación x*3 es el componente azul, el byte de la ubicación x*3+1 es el componente verde y el x*3+2 es el rojo. Entonces si quisiéramos poner una imagen en color negro habría que hacer lo siguiente: VAR p: PByteArray; x: Integer; y: Integer; BEGIN { Iterar entre todas las líneas} FOR y := 0 TO Pred(FImage.Height) DO BEGIN p := FImage.ScanLine[y]; FOR x := 0 TO Pred(FImage.Width) DO BEGIN p[x * 3] := 0; p[x * 3 + 1] := 0; p[x * 3 + 2] := 0; END; END; END; Ahora vamos a profundizar un poco más en este aspecto, hemos visto que cambiando los valores de los bytes de un pixel podemos conseguir los diferentes tipos de colores y si vamos un poco más allá conseguiremos espectaculares efectos visuales como los siguientes: - Solarize VAR p: PByteArray; x: Integer; y: Integer; BEGIN FOR y := 0 TO Pred(FImage.Height) DO BEGIN p := FImage.ScanLine[y]; FOR x := 0 TO Pred(FImage.Width) DO BEGIN IF p[x * 3] > 127 THEN p[x * 3] := 255 - p[x * 3]; IF p[x * 3 + 1] > 127 THEN p[x * 3 + 1] := 255 - p[x * 3 + 1]; IF p[x * 3 + 2] > 127 THEN p[x * 3 + 2] := 255 - p[x * 3 + 2]; END; END; END; - Invertir colores VAR p: PByteArray; x: Integer; y: Integer; BEGIN FOR y := 0 TO Pred(FImage.Height) DO BEGIN { get the pointer to the y line } p := FImage.ScanLine[y]; FOR x := 0 TO Pred(FImage.Width) DO BEGIN { modificar el color azul } p[x * 3] := 255 - p[x * 3]; { modificar el color verde } p[x * 3 + 1] := 255 - p[x * 3 + 1]; { modificar el color rojo } p[x * 3 + 2] := 255 - p[x * 3 + 2]; END; END; END; - Convertir a escala de grises Se hace con la siguiente fórmula Gray = (Red * 3 + Blue * 4 + Green * 2) div 9 VAR p: PMyPixelArray; x: Integer; y: Integer; gray: Integer; BEGIN FOR y := 0 TO Pred(FImage.Height) DO BEGIN p := FImage.ScanLine[y]; FOR x := 0 TO Pred(FImage.Width) DO WITH p[x] DO BEGIN gray := (Red * 3 + Blue * 4 + Green * 2) DIV 9; Blue := gray; Red := gray; Green := gray; END; END; END; - Hacer un espejo vertical procedure TForm1.Button1Click(Sender: TObject); procedure EspejoVertical(Origen,Destino:TBitmap); var x,y : integer; Alto : integer; Po,Pd : PByteArray; tmpBMP : TBitmap; LongScan : integer; begin {Si es un modo raro... pasamos} case Origen.PixelFormat of pfDevice, pfCustom: Raise exception.create( 'Formato no soportado'+#13+ 'Bitmap Format not valid'); end; {Calculamos variables intermedias} Alto :=Image1.Picture.Bitmap.Height-1; try {Calculo de cuánto ocupa un scan} LongScan:= Abs( Integer(Origen.ScanLine[0])- Integer(Origen.ScanLine[1]) ); except Raise exception.create( 'ScanLine Error...'); end; {Cremos un bitmap intermedio} tmpBMP:=TBitmap.Create; with tmpBMP do begin {Lo asignamos, así se copia la paleta si la hay} Assign(Origen); {Esto es para que sea un bitmap nuevo... es un bug de D3 y D4} Canvas.Pixels[0,0]:=Origen.Canvas.Pixels[0,0]; end; {Damos la vuelta al bitmap} for y:=0 to Alto do begin Po := Origen.ScanLine[y]; Pd := tmpBMP.ScanLine[Alto-y]; for x := 0 to LongScan-1 do begin Pd^[X]:=Po^[X]; end; end; {Lo asignamos al bitmap destino} Destino.Assign(tmpBMP); {Esto es para parchear un bug de Delphi 3 y Delphi4...} Destino.Canvas.Pixels[0,0]:=tmpBMP.Canvas.Pixels[0,0]; tmpBMP.Free; end; begin EspejoVertical( Image1.Picture.Bitmap, Image1.Picture.Bitmap); Image1.Refresh; end; - Ajustar el brillo Se hace con la siguiente fórmula: NewPixel = OldPixel + (1 * Percent) div 200 FUNCTION IntToByte(AInteger: Integer): Byte; INLINE; BEGIN IF AInteger > 255 THEN Result := 255 ELSE IF AInteger < 0 THEN Result := 0 ELSE Result := AInteger; END; PROCEDURE TMainForm.AdjustBrightness(Percent: Integer); VAR p: PMyPixelArray; x: Integer; y: Integer; amount: Integer; BEGIN amount := (255 * Percent) DIV 200; FOR y := 0 TO Pred(FImage.Height) DO BEGIN p := FImage.ScanLine[y]; FOR x := 0 TO Pred(FImage.Width) DO WITH p[x] DO BEGIN Blue := IntToByte(Blue + amount); Green := IntToByte(Green + amount); Red := IntToByte(Red + amount); END; END; Invalidate; END; PROCEDURE TMainForm.ScanLineBrightnessClick(Sender: TObject); VAR amount: Integer; BEGIN amount := StrToInt(InputBox('Brightness Level', 'Enter a value from -100 to 100:', '50')); AdjustBrightness(amount); END;Suscribirse : ![]() ![]() ![]() ![]() http://feeds.feedburner.com/~r/Delph...~4/U5Qo0KeBDJw ![]() |
Alle Zeitangaben in WEZ +1. Es ist jetzt 04:41 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz