Autor Tema: Rellenar con FloodFill usando GDI+ en VB6  (Leído 5966 veces)

0 Usuarios y 1 Visitante están viendo este tema.

TOLO68

  • Kilobyte
  • **
  • Mensajes: 60
  • Reputación: +2/-0
    • Ver Perfil
Rellenar con FloodFill usando GDI+ en VB6
« en: Febrero 16, 2017, 07:05:38 pm »
Buenas a Todos
Tengo este codigo en VB6 que crea un poligono con lineas
he probado en otro codigo tambien con lineas seguidas de arcos y demas y funciona
lo que hace es rellenar el interior del dibujo en un color

-------------------------------------------------------------------------

Private Declare Function FloodFill Lib "gdi32" (ByVal hdc As Long, ByVal X As Long, ByVal Y As Long, ByVal crColor As Long) As Long

Sub Form_Click()

   ScaleMode = 3
   ForeColor = vbBlue
   Line (50, 50)-(300, 50)
   Line -(200, 200)
   Line -(50, 150)
   Line -(50, 50)
   FillStyle = 0
   FillColor = vbBlue
   FloodFill Form1.hdc, 53, 53, vbBlue

End Sub

-------------------------------------------------------------------------

La pregunta es si conoceis alguna manera de hacerlo en VB6 pero con GDI+ por el tema del antialias, ya que he buscado por internet y dicen que no existe el FloodFill en GDI+, algunos dicen que hay que hacerlo con una funcion usando getpixel y setpixel

Esto es porque tengo que hacer dibujos irregulares con diferentes formas, por ejemplo algo como una estrella con puntas redondeadas( las puntas serian arcos), y el resto lineas, por eso necesito lo del floodfill para primero crear el dibujo y luego rellenarlo dandole las coordenadas X,Y en el interior como lo hace en el ejemplo anterior

Muchas Gracias a Todos


LeandroA

  • Administrador
  • Petabyte
  • *****
  • Mensajes: 1128
  • Reputación: +151/-8
    • Ver Perfil
Re:Rellenar con FloodFill usando GDI+ en VB6
« Respuesta #1 en: Febrero 16, 2017, 10:25:35 pm »
yo no conosco con GDI+, pero la pregunta es si no es el usuario quien decidiria el punto donde pintar, no tiene mucho sentido, se puede hacer algo mediante el array de bits de la imagen pero despues te enfrentarias a el problema del antialias donde o se perderia o quedaria para el traste.

No sera que vos queres rellenar un polígono? si es eso es lo que puse el otro dia y ahi si lo veo mas viable.

NEBIRE

  • Kilobyte
  • **
  • Mensajes: 57
  • Reputación: +7/-1
    • Ver Perfil
Re:Rellenar con FloodFill usando GDI+ en VB6
« Respuesta #2 en: Febrero 16, 2017, 10:47:11 pm »
Tolo68, cuando hablábamos de la transparencia (una semanas atrás), te decía que no te habías quedado con la lección importante... las máscaras.

Una vez que aprendes a crear máscaras y usar transparencias se puede hacer infinidad de cosas, con solo un poco de imaginación (y también prueba y error, por supuesto).

El caso es que dada una figura, y que la quieras rellenar de algo, se debe crear una máscara partiendo de la figura. La máscara es como un molde positivo, luego se crea el molde negativo, esto es se invierte la máscara, entonces ahora la superficie ocupada es precisamente la que quieres rellenar, entonces ahora esa superficie si necesitas pintarla, rellenarla con una brocha, etc... lo haces, y luego fundes ambas imágenes (en este caso sin transparencia (pero de igual modo es una operación ROP, de fusión))... la original con la que original modificada a máscara, invertida y coloreada/enbrochada... y todas esas operaciones se hacen con funciones que más o menos ya debes conocer en GDI+ o al menos GDI.

El uso de máscaras y transparencias, es como sumas y restas de superficies, tienes que mirarlo que es igual a las operaciones lógicas (or and, not, xor, not and, not or, not xor), con los bytes.... la unica diferencia es que es a una escala mayor, y que definen una superficie, etc.... Si aprendes a sumar, restar superficies, invertirlas, hacerlas negras y blancas, etc... habras aprendido la mitad o más delo que necesitas para dominar gráficos (la otra mitad es entender las funciones que ya existen en alguna librería o crearlas por tu propia cuenta). y... bueno, un pequeño porcentaje también es necesario para entender los píxeles, el color... que también son bytes y aplicar operaciones lógicas...

Yo no estoy muy puesto en GDI+, normalmente me basta con GDI, pero GDI+ es una vuelta más de rosca, en cuanto a calidad, velocidad y más funcionalidad...
Si entiendes el proceso, de lo que hay que hacer, es sólo cuestión de apilar las funciones que necesitas usar.... aunque no exista una función específica, aplicando varias funciones en pasos sucesivos, se consiigue la funcionalidad deseada.... de eso trata GDI, de tener 'funciones primitivas' para hacer cosas complejas partiendo de funciones más simples y archiconocidas.
« última modificación: Febrero 16, 2017, 10:53:49 pm por NEBIRE »

NEBIRE

  • Kilobyte
  • **
  • Mensajes: 57
  • Reputación: +7/-1
    • Ver Perfil
Re:Rellenar con FloodFill usando GDI+ en VB6
« Respuesta #3 en: Febrero 16, 2017, 11:16:42 pm »
Si quieres porgramar tu mismo el allgoritmo a bajo nivel, puede interesarte una pagina como esta:
https://es.wikipedia.org/wiki/Algoritmo_de_relleno_por_difusi%C3%B3n

Nota que Flood fill, tiene muchas variaciones, la más conocida se llamaa Boundary fill, y si tiene degradado, incluso se llama Fountain Fill...

La ventaja de hacerlo manualmente, es que solo requiere una única operación, frente a las varias consecutivas trabajando con máscaras, por tanto al final tu algoritmo puede ser más rápido incluso aunque el algoritmo no esté optimizado del todo... Aún así, requiere una buena optimización...

Si quieres implementarlas por tu cuenta, he aquí un esquema del algoritmo (muy, muy sencillito):
http://web.cs.ucdavis.edu/~ma/ECS175_S00/Notes/0411_a.pdf

Aquí un enlace, que explica similar al pdf:
http://www.siggraph.org/education/materials/HyperGraph/scanline/outprims/polygn6a.htm

TOLO68

  • Kilobyte
  • **
  • Mensajes: 60
  • Reputación: +2/-0
    • Ver Perfil
Re:Rellenar con FloodFill usando GDI+ en VB6
« Respuesta #4 en: Febrero 17, 2017, 07:05:00 am »
Hola a todos de nuevo, gracias por las respuestas, lo que dice LEANDRO de ser el usuario que decide el punto donde pintar, eso seria para un programa de dibujo tipo paint, pero aqui lo elijo yo porque es para los controles que estoy haciendo, pero veo que has entendido lo que queria :), que seria parecido al bote de pintura del paint.

Lo de rellenar el poligono ya lo estoy usando para hacer una aguja de un Gauge, ( un circulo con relleno en el centro , y luego un triangulo fino y largo con el poligono para la aguja, y voy rotando los puntos con Sin y Cos del triangulo), a ver si os puedo hacer una captura de pantalla para que lo veais

Lo que comenta NEBIRE sobre las mascaras tambien las he usado en algunos controles como el Display hexadecimal o el Display Matrix, para poder elegir entre 16 millones de colores para el color del LED, fue lo mejor porque antes tenia solo 3 colores, los 3 bitmaps)

os dejo aqui el link de la web para que podais ver las capturas de pantalla.
http://windevelop.hol.es

La pagina esta en desarrollo pero poco a poco la voy haciendo, asi como voy acabando los controles y creando algunos nuevos, asi ya se va posicionando en GOOGLE.

El unico que puede descargarse es el DisplayHEX.

Lo de usar el algoritmo para el antialias tambien se me habia ocurrido. :)
o incluso usar Bezier para hacer curvas, que creo que si se puede rellenar igual que el poligono

En el Knob hice el circulo antialias con solo GDI32 , con un algoritmo que encontre por la red, creo que era el sistema de WU o algo asi.

La cuestion es ir buscando ideas

Bueno, gracias de nuevo por vuestra ayuda. :)

 

Virgil Tracy

  • Kilobyte
  • **
  • Mensajes: 64
  • Reputación: +38/-1
    • Ver Perfil
Re:Rellenar con FloodFill usando GDI+ en VB6
« Respuesta #5 en: Febrero 17, 2017, 02:20:53 pm »
¿ Y usando path ?  :P

Código: (VB) [Seleccionar]
Option Explicit


Private Declare Function CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (Dest As Any, Src As Any, ByVal cb As Long) As Long
Private Declare Function GdiplusStartup Lib "gdiplus" (token As Long, inputbuf As GDIPlusStartupInput, Optional ByVal outputbuf As Long = 0) As Long
Private Declare Function GdiplusShutdown Lib "gdiplus" (ByVal token As Long) As Long
Private Declare Function GdipCreateFromHDC Lib "gdiplus" (ByVal hDC As Long, graphics As Long) As Long
Private Declare Function GdipSetSmoothingMode Lib "gdiplus" (ByVal graphics As Long, ByVal SmoothingMd As SmoothingMode) As Long
Private Declare Function GdipDeleteGraphics Lib "gdiplus" (ByVal graphics As Long) As Long
Private Declare Function GdipCreatePath Lib "gdiplus" (ByVal brushmode As FillMode, path As Long) As Long
Private Declare Function GdipAddPathLine Lib "gdiplus" (ByVal path As Long, ByVal x1 As Single, ByVal y1 As Single, ByVal x2 As Single, ByVal y2 As Single) As Long
Private Declare Function GdipFillPath Lib "gdiplus" (ByVal graphics As Long, ByVal Brush As Long, ByVal path As Long) As Long
Private Declare Function GdipDrawPath Lib "gdiplus" (ByVal graphics As Long, ByVal pen As Long, ByVal path As Long) As Long
Private Declare Function GdipCreateSolidFill Lib "gdiplus" (ByVal argb As Long, Brush As Long) As Long
Private Declare Function GdipCreatePen1 Lib "gdiplus" (ByVal color As Long, ByVal Width As Single, ByVal unit As GpUnit, pen As Long) As Long
Private Declare Function GdipDeleteBrush Lib "gdiplus" (ByVal Brush As Long) As Long
Private Declare Function GdipDeletePen Lib "gdiplus" (ByVal pen As Long) As Long
Private Declare Function GdipDeletePath Lib "gdiplus" (ByVal path As Long) As Long


Private Type GDIPlusStartupInput
    GdiPlusVersion                      As Long
    DebugEventCallback                  As Long
    SuppressBackgroundThread            As Long
    SuppressExternalCodecs              As Long
End Type



Private Enum FillMode
   FillModeAlternate        ' 0
   FillModeWinding          ' 1
End Enum

Private Enum GpUnit  ' aka Unit
   UnitWorld      ' 0 -- World coordinate (non-physical unit)
   UnitDisplay    ' 1 -- Variable -- for PageTransform only
   UnitPixel      ' 2 -- Each unit is one device pixel.
   UnitPoint      ' 3 -- Each unit is a printer's point, or 1/72 inch.
   UnitInch       ' 4 -- Each unit is 1 inch.
   UnitDocument   ' 5 -- Each unit is 1/300 inch.
   UnitMillimeter ' 6 -- Each unit is 1 millimeter.
End Enum

' Quality mode constants
Private Enum QualityMode
   QualityModeInvalid = -1
   QualityModeDefault = 0
   QualityModeLow = 1       ' Best performance
   QualityModeHigh = 2      ' Best rendering quality
End Enum

Private Enum SmoothingMode
   SmoothingModeInvalid = QualityModeInvalid
   SmoothingModeDefault = QualityModeDefault
   SmoothingModeHighSpeed = QualityModeLow
   SmoothingModeHighQuality = QualityModeHigh
   SmoothingModeNone
   SmoothingModeAntiAlias
End Enum

Dim GdipToken As Long

Private Sub Form_Click()
Dim pGraphics As Long
Dim path As Long
Dim crSolid As Long
Dim penOutLine As Long

Call GdipCreateFromHDC(Me.hDC, pGraphics)
Call GdipSetSmoothingMode(pGraphics, SmoothingModeHighQuality)

Call GdipCreatePath(FillModeAlternate, path)
Call GdipAddPathLine(path, 50, 50, 300, 50)
Call GdipAddPathLine(path, 300, 50, 200, 200)
Call GdipAddPathLine(path, 200, 200, 50, 150)
Call GdipAddPathLine(path, 50, 150, 50, 50)

Call GdipCreateSolidFill(ConvertColor(vbBlue, 100), crSolid)
Call GdipCreatePen1(ConvertColor(vbBlue, 255), 0.8, UnitWorld, penOutLine)

Call GdipFillPath(pGraphics, crSolid, path)
Call GdipDrawPath(pGraphics, penOutLine, path)


Call GdipDeleteBrush(crSolid)
Call GdipDeletePen(penOutLine)
Call GdipDeletePath(path)

Me.Refresh

Call GdipDeleteGraphics(pGraphics)

End Sub

Private Sub Form_Load()

InitGDI

Me.ScaleMode = vbPixels
Me.AutoRedraw = True
Me.BackColor = vbWhite

End Sub

Private Sub Form_Unload(Cancel As Integer)

TerminateGDI

End Sub








Private Function ConvertColor(color As Long, alpha As Long) As Long
Dim argb(0 To 3) As Byte

argb(3) = CByte(alpha)
argb(0) = ((color \ &H10000) And &HFF) 'blue
argb(1) = ((color \ &H100) And &HFF) 'green
argb(2) = (color And &HFF) 'red
CopyMemory ConvertColor, argb(0), 4&

End Function


'Inicia GDI+
Private Sub InitGDI()
    Dim GdipStartupInput As GDIPlusStartupInput
    GdipStartupInput.GdiPlusVersion = 1&
    Call GdiplusStartup(GdipToken, GdipStartupInput, ByVal 0)
End Sub
 
'Termina GDI+
Private Sub TerminateGDI()
    Call GdiplusShutdown(GdipToken)
End Sub


NEBIRE

  • Kilobyte
  • **
  • Mensajes: 57
  • Reputación: +7/-1
    • Ver Perfil
Re:Rellenar con FloodFill usando GDI+ en VB6
« Respuesta #6 en: Febrero 22, 2017, 01:38:30 pm »
Lo que comenta NEBIRE sobre las mascaras tambien las he usado en algunos controles como el Display hexadecimal o el Display Matrix, para poder elegir entre 16 millones de colores para el color del LED, fue lo mejor porque antes tenia solo 3 colores, los 3 bitmaps)

os dejo aqui el link de la web para que podais ver las capturas de pantalla.
http://windevelop.hol.es

El unico que puede descargarse es el DisplayHEX.
mmm... al leerte, me parece entender que usas las máscaras a nivel de byte, para elegir color. Aunque es la misma esencia, aplicado a una superficie, el concepto cambia. Creo que no hablamos de lo mismo...
Una máscara de bits, sirve para permitir que determinados bits, sigan ahí, o para filtrarlos y por tanto que permanezca el resto menos esos...
Una máscara de imagen (hace lo mismo), opera sobre un array de píxeles y en esencia esa máscara contiene solo dos valores en sus píxeles: blanco (todos los bits a 1) o negro (todos los bits a cero). La imagen de máscara es luego operada pixel a píxel con otra imagen para muy diferentes efectos, desde las transparencias hasta definir regiones (añadir, excluir regiones)... con el uso de la máscara, básicamente cada píxel de ella, 'decide' si se acepta o rechaza por completo, el píxel de destino, ya que:
'1' and píxel = píxel
'0' and pixel = 0
'1' or píxel = 1
'0' or pixel = pixel
Y con solo esas 4 operaciones, pueden desde crearse máscaras, a bloquear píxeles, asignarlos, o convertirlos en blanco o negro (esto es crear otra máscara, partiendo de una imagen y otra máscara, lo que sirve para añadir o eliminar regiones, permitiendo el 'clipping' sobre regiones).

En un control tan simple, como un display LCD, no tiene mucho sentido usar máscaras (de imagen), puede dibujarse perfectamente solo con el método line. Ni siquiera hace falta recurrir a las APIs GDI, para ese control, el método Line es suficientemente rápido, como para no requerir nada más. Redibujar el control probablemente no requiera más de una diezmilésima de segundo, aunque GDI, lo redujera a la mitad o a un tercio, es imperceptible a esa escala.

------------------------------------------------------

...luego de mirar tu página y bajando ese control, me ha sorprendido  :o negativamente  :o el enorme tamaño que tiene compilado... la friolera de 204kb. y tras ponerlo en un proyecto, me  :-[ decepciona   :-[ aún más, que básicamente tiene solo  :'( 7 propiedades específicas  :'( del control: 3 para elegir el color, 1 para preselecciones de colores, Digitos, Value, SizeDisplay y la innecesaria (a mi juicio) LeftZero). Muy poca funcionalidad y opciones para 204kb.
En general lo que hace aumentar el tamaño de un control son los recursos, cuando uno añade muchas imágenes, sonidos o textos. Pero el control carece de sonido, no necesita imágenes y tampoco  :( están documentadas  :( las propiedades. :(

-------------------------------------

Se me ha ocurrido hacer un control, del mismo tipo, un Display-LCD de 7 segmentos... y bueno, le he añadido todas las propiedades que se me han ocurido, sumo hasta 18, con muchas opciones (por ejemplo el tamaño, va de 0 a 12 (no de 0-2 como en el tuyo), los digitos van de 0-255 tamaño de 1 byte, si también 0, el tuyo va de 1-32, aunque puede valer porqué limitarlo tanto, cómo sabemos que alguien no fuera a necesitar 40 caracteres. El tamaño de byte, es un límite perfecto para estos casos?
En mi caso, la cantidad de tamaños posibles junto a otra propiedad 'proporción' (que decide el valor de cuadrícula, de los dígitos para el ancho y alto (9x5, 9x6, 8x5,10x7, etc...hasta 21 distintos)) permiten un total de 13x21 = 273 combinaciones diferentes). En mi control el tamaño en realidad lo que define es el grosor del segmento... lo que hace que el tamaño final del control dependa también del valor de 'proporcion' y el número de dígitos. También añadí otra propiedad DigitoDecim, que actúa como si fueran decimales, aunque básicamente lo que permite es elegir otro color para ellos...

En fin tal como figura en la imagen que adjunto de ejemplo...


y después de compilado, solo pesa 60kb. teniendo en cuenta que si solo compilas un control en VB6, sin nada de código, tiene un tamaño de 20kb.
Se puede afirmar que el código ocupa solo 40kb. y eso que le he metido todas las propiedades que he imaginado (y media docena de eventos)... incluso una llamada 'displayTime', que al activarla a True, el control 'se convierte' en un reloj digital, que se actualiza cada segundo. Incluso una propiedad para elegir que base numérica soportar: Octal, Decimal o Hexadecimal... Caracteres como el guión bajo, medio y alto están disponibles en todos los casos.
Total, que si a tu control le quitamos esos 20 kb. se queda en 184, frente a los 40 del mío.... siento enorme curiosidad por saber que código contiene para estar inflado 4'5 veces el tamaño, con sólo la cuarta-quinta parte de la funcionalidad, que yo le he puesto a éste...

----------------------------------------------


...y bueno, se me ocurre que ya que me he tomado la molestia de hacer este control, documentaré todas las propiedades, y cuando lo recompile, lo dejaré disponible como recurso en el foro...  8) 8) 8)

« última modificación: Febrero 22, 2017, 01:56:19 pm por NEBIRE »

TOLO68

  • Kilobyte
  • **
  • Mensajes: 60
  • Reputación: +2/-0
    • Ver Perfil
Re:Rellenar con FloodFill usando GDI+ en VB6
« Respuesta #7 en: Febrero 23, 2017, 11:11:14 am »
Hola NEBIRE

El control que descargaste en la web, es el control antiguo que tenia, lo puse para ver las descargas que tiene, que en mi web solo ha tenido 5 o 6, pero en otras webs ya ha tenido 250, y de la web de controles que tenia antiguamente tuvo casi 3000, que no son muchas, pero claro es un control OCX, no es como un compresor ZIP o una utilidad de este tipo, que es usado en todos los ordenadores.

Lo que pasa es que cerraron el Hosting y ya no volví a poner la web en otro.

Por esto solo puse el DisplayHEX para poder descargar, porque asi puedo ver, si todavia la gente esta interesada en este tipo de controles.

Lo de la documentacion tambien tengo que hacerlo, y tambien tengo preparados los archivos PAD para los submit, pero claro todo eso lleva su tiempo :)

La Curiosidad que tenias de los 200K del control, era porque solo los bitmaps para las mascaras (habia 3, 1 por cada tamaño de display) ya ocupaban alrededor de 170K, porque los guarde en BMP a 16 millones de colores cuando basta guardarlos a 2 colores, con lo cual me han pasado a ocupar unos 10K :), algo que no tuve en cuenta anteriormente.

Ya que guardandolos en JPG aveces salen manchas o pixeles que no se ven a simple vista pero hacen funcionar mal la mascara.

De todas formas hoy en dia 200K no es nada para los discos duros que hay con cientos de GB, pero bueno ya se que tengo que hacer para otra vez :)

Lo de los digitos de 1 a 32, tambien lo podria hacer hasta 255, solo cambiando el 32 por 255 en una parte del codigo

Lo puse a 32, porque asi y todo encontre ya una exageracion un display de 32 digitos o mas.

Lo de no usar mascaras y hacerlo con lineas tambien tengo idea de hacerlo asi, y poder poner todos los tamaños que quiera.

De todas formas tambien me gustaria ver el tuyo, una vez que lo tengas acabado, y poder hacer comparaciones de velocidad y cosas así.

Asi competimos a ver quien hace el mejor display :D

Saludo desde España

:)

NEBIRE

  • Kilobyte
  • **
  • Mensajes: 57
  • Reputación: +7/-1
    • Ver Perfil
Re:Rellenar con FloodFill usando GDI+ en VB6
« Respuesta #8 en: Febrero 23, 2017, 09:35:34 pm »
jejeje... Si no se trata de competir. Se trata de hacer las cosas con sensatez.

Aún recuerdo aquellas palabras de Guillermo Puertas, cuando allá por los 80 soltó aquello de "64kb. (de memoria) debieran bastar para todos"... y ya ves, hoy la media ronda los 4gb. y parece ser insuficiente.

Los dias de la semana, es razonable limitarlos a 7, porque no hay más... pero si no hay complejidad añadida por el hecho de una mayor variedad, y tampoco acontece un problema de desbordamiento (255 digitos por 128pixeles que llegara a tener de ancho como maximo=32640, no llegando al limite del integer, no habría desbordamiento), es mejor no limitarlo basado en la creencia de que "deberia ser suficiente". Si no que dejarlo al límite natural. Si el 99% solo usará 4-8 digitos, que tenga un limite alto, no daña a nadie, a la vez que a ese 1%, no le fastidias... que luego andara como loco buscando una solución.

En general cuando uses imágenes,  escalarlas a mayor tamaño, suele dar mucho mejor resultado que a menor tamaño. El "downsampling", a pocos pixeles es letal, no hay manera de acertar con los detalles que son importantes. En estos casos es mejor "pintar" directamente, para tener un control férreo de la apariencia resultante. Poco importa que tengas una imagen buenísima a 512 píxeles, si hay que reducirla a 32, no tienes ningún control de qué detalles se perderán y cuales quedarán... sobretodo si ni siquiera el algoritmo de escalado es tuyo...

Ten en cuenta, que la calidad del control, exigirá buenos detalles. Ofrece variedad de propiedades, que el cliente no sienta que el control está demasiado limitado, porque si lo siente asi, buscara un sustituto... Huye también de darle funcionalidades peregrinas, que nunca nadie va a usar, queriendo justificar con ello, que "es mejor que aquel otro", por que "no tiene esto". Por ejemplo un display LCD, no necesita para nada un menú emergente, ni siquiera un ToolTipText.

Pero si puede aceptar de buen grado animarlo... desplazando el texto a derecha o izquierda, saliendo por el otro extremo y volviendo a entrar... de ese modo un texto/valor que no cabe en el display, se acaba viendo. Que es precisamente lo último que le haré yo al control, el próximo fin de semana.