[5.20b] Système de dalle pour drawing 2D

Partagez votre expérience de PureBasic avec les autres utilisateurs.
Avatar de l’utilisateur
blendman
Messages : 2017
Inscription : sam. 19/févr./2011 12:46

[5.20b] Système de dalle pour drawing 2D

Message par blendman »

salut

J'ai retrouvé un ancien fichier que j'ai essayé de modifier pour l'optimiser un peu.
Il s'agit d'un système de "dalles" pour du painting2D (pour un soft comme photoshop, gimp, ou autre).

Voici une première version du code, non optimisée et non terminée.
N'hésitez pas à poster si vous changez des choses dessus, apporté des améliorations, etc ;)

PB : 5.20beta 14

Code : Tout sélectionner

;{ infos
; test de dalle (pour painting 2D ou autre)
; dev :blendman 08/2011 - revu en 08/2013
; pb  : 5.20 beta14
;}

;{ constantes
;window
Enumeration
  #Win_0
EndEnumeration

; menu
Enumeration 
  #menu_Sortie
EndEnumeration

; gadget
Enumeration
  #SA_surfaceDessin
  #canvas
  ; Brush
  #TB_BrushSize
  #TB_BrushAlpha
  #Button_BrushColor
EndEnumeration

; Image
Enumeration
  #image_dessinfinal
  #imageDessin
EndEnumeration

;}

;{ Structures
Structure s_dalle
  id.a
  position.point
  image.w
  width.w
  height.w
  touched.a
EndStructure
Global NewMap dalle.s_dalle()
Structure Brush
  Size.i
  Alpha.a
  Color.i
EndStructure
Global Brush.Brush
brush\Size = 10
brush\Alpha = 150
brush\color = RGBA(0,0,0,brush\Alpha)

;}

;{ variables
Global doc_H.w = 2000, doc_W.w = 2000, draw.b, xx.w, yy.w, globalID.a, winW.w,winH.w
Global Size.w = 128, Arx.w, ArY.w, ScrollH.w,ScrollW.w
;}

;{ Procedure
Procedure DrawBG(mode=0, x1=0,y1=0)
  Shared first  
  key$ =Str((x1-1)/Size)+"/"+Str((y1-1)/Size)
  
  Select mode 
    Case 0 ;  classique 
      If StartDrawing(ImageOutput(#image_dessinfinal))
        If first = 0
          Box(0,0,ScrollW,ScrollH,RGBA(255,255,255,0))    
          first =1
        EndIf
        DrawingMode(#PB_2DDrawing_AlphaBlend)
        Box(0,0,ScrollW,ScrollH,RGBA(125,255,255,0))
        DrawAlphaImage(ImageID(dalle(key$)\image),dalle(key$)\position\x - ArX,dalle(key$)\position\y - ArY)
        StopDrawing()
      EndIf
      
    Case 1; quand on déplace l'area
      If StartDrawing(ImageOutput(#image_dessinfinal))
        Box(0,0,ScrollW,ScrollH,RGBA(255,255,255,0))    
        DrawingMode(#PB_2DDrawing_AlphaChannel); d'abord pour effacer
        Box(0,0,ScrollW,ScrollH,RGBA(125,255,255,0))
        DrawingMode(#PB_2DDrawing_AlphaBlend)
        ForEach dalle()
          DrawAlphaImage(ImageID(dalle()\image), dalle()\position\x - ArX, dalle()\position\y - ArY)            
          dalle()\touched = 0            
        Next 
        StopDrawing()
      EndIf
      
      If StartDrawing(CanvasOutput(#canvas))
        DrawingMode(#PB_2DDrawing_AlphaBlend)
        DrawImage(ImageID(#image_dessinfinal),ArX,ArY)
        StopDrawing()
      EndIf
      
    Case 2
      
      
  EndSelect
EndProcedure

Procedure update_drawing(xx=0,yy=0,mode = 0)
  key$ =Str((xx-1)/Size)+"/"+Str((yy-1)/Size)
  If mode = 1
    DrawBG(1,xx,yy)
  Else  
    If StartDrawing(CanvasOutput(#canvas))
      DrawingMode(#PB_2DDrawing_Default)
      Box(0,0,ScrollW,ScrollH,RGBA(255,255,255,255))
      DrawImage(ImageID(#image_dessinfinal),ArX,ArY)
      DrawAlphaImage(ImageID(dalle(key$)\image),dalle(key$)\position\x,dalle(key$)\position\y)
      StopDrawing()
    EndIf
  EndIf
EndProcedure
;}

;{ openwindow
ExamineDesktops()
winW = DesktopWidth(0)
winH = DesktopHeight(0)
ScrollW = winW-150-150
ScrollH = winH-120

If OpenWindow(#Win_0, 0,0,winW,winH,"Animatoon-test dalle", #PB_Window_SystemMenu|#PB_Window_MinimizeGadget|#PB_Window_MaximizeGadget|#PB_Window_WindowCentered|#PB_Window_Maximize|#PB_Window_Minimize)
  
  ;{ Menus
  MenuBar=CreateMenu(#PB_Any,WindowID(#Win_0))
  MenuTitle("Fichier")
  MenuItem(#menu_Sortie,"Quitter")
  
  ;}
  ;{ Status barre
  StatusBar=CreateStatusBar(#PB_Any,WindowID(#Win_0))
  AddStatusBarField(400)
  AddStatusBarField(200)
  ;}
  ;{ Gadget & images
  ; creation des dalles (256*256), en fonction de la taille du document
  nb_dalle_w = Round(doc_w/Size,0)
  nb_dalle_h = Round(doc_h/Size,0) 
  
  ; on crée le système de dalle, à revoir sans doute. Mais on comprend l'idée/
  ; on pourrait créé un canvas par dalle sans doute.
  For i = 0 To nb_dalle_w ; on commence les images à 1000, ça laisse de la marge au cas où on aurait besoin de 1000 images pour le logiciel
    For j = 0 To nb_dalle_h     
      ; creation of the image for the dalle
      If CreateImage(#imageDessin+a, Size,Size,32)
        If StartDrawing(ImageOutput(#imageDessin+a))
          DrawingMode(#PB_2DDrawing_AlphaChannel)
          Box(0,0,Size,Size,RGBA(0,0,0,0))
          DrawingMode(#PB_2DDrawing_AlphaBlend)
          Box(0,0,Size,Size,RGBA(155-Random(50),155-Random(50),155-Random(50),255))
          StopDrawing()
        EndIf
      EndIf  
      ; add the dalle on the list dalle()
      AddMapElement(dalle(),Str(i)+"/"+Str(j))
      dalle()\id = a 
      dalle()\position\y = j * Size
      dalle()\position\x = i * Size
      dalle()\width = Size
      dalle()\height = Size
      dalle()\image = #imageDessin+a
      a + 1
    Next j
  Next i
  
  ; creation de l'image (support) pour le dessin
  CreateImage(#image_dessinfinal,ScrollW,ScrollH,32, #PB_Image_Transparent)
  DrawBG()
  
  ; brush tools
  TrackBarGadget(#TB_BrushSize,5,10,100,20,1,500)
  GadgetToolTip(#TB_BrushSize,"Régler la taille de la brosse")
  SetGadgetState(#TB_BrushSize,brush\Size)
  TrackBarGadget(#TB_BrushAlpha,5,40,100,20,0,255)
  GadgetToolTip(#TB_BrushAlpha,"Régler l'opacité de la brosse")
  SetGadgetState(#TB_BrushAlpha,brush\Alpha)
  ButtonGadget(#Button_BrushColor, 5,70,20,20,"")
  GadgetToolTip(#Button_BrushColor,"Choisir la couleur de la brosse")
  ; scrollareagadget & canvas // surface de dessin

  ScrollAreaGadget(#SA_surfaceDessin,150,10,ScrollW,ScrollH,doc_W,doc_H,10,#PB_ScrollArea_Flat)
  CanvasGadget(#canvas,0,0,doc_W,doc_H, #PB_Canvas_ClipMouse); ou créer plein de petits canvas, ce serait plus rapide ?
  CloseGadgetList()
  update_drawing()
  
  ;}
  
EndIf

;}

;{ Boucle
Repeat
  EventID  = WaitWindowEvent()
  MenuID   = EventMenu()
  GadgetID = EventGadget()
  WindowID = EventWindow()
  
  ;{ general
  XX = (WindowMouseX(#Win_0)-150 + ArX)
  YY = (WindowMouseY(#Win_0) -10 + ArY)
  ;}
  
  Select EventID
      
    Case #PB_Event_Menu
      Select MenuID
        Case #menu_Sortie
          quit.b=1
      EndSelect
      
    Case #PB_Event_Gadget
      Select GadgetID   
          
        Case #SA_surfaceDessin
          ArX = GetGadgetAttribute(#SA_surfaceDessin,#PB_ScrollArea_X)
          ArY = GetGadgetAttribute(#SA_surfaceDessin,#PB_ScrollArea_Y)
          update_drawing(xx,yy, 1)
          
        Case #canvas          
          Select EventType()
              
            Case #PB_EventType_MouseMove 
              StatusBarText(StatusBar,0,"Coords : "+Str(XX)+","+Str(YY) + " / Dalle : "+Str(Dx)+"/"+Str(Dy))
              If draw = 1
                Dx = (xx-1)/Size
                Dy = (yy-1)/Size   
                If D2x <> Dx Or D2y <> Dy
                  D2x = Dx
                  D2y = Dy
                  DrawBG(0,XX1,YY1)
                EndIf                
                key$ =Str((xx-1)/Size)+"/"+Str((yy-1)/Size)
                dalle(key$)\touched =1
                If StartDrawing(ImageOutput(dalle(key$)\image))
                  DrawingMode(#PB_2DDrawing_AlphaBlend)
                  Circle(XX-dalle(key$)\position\x ,YY-dalle(key$)\position\y ,brush\Size,brush\Color)
                  StopDrawing()          
                EndIf  
                D2x =Dx
                D2y =Dy
                XX1 = XX
                YY1 = YY
                update_drawing(xx,yy)
              EndIf
              
            Case #PB_EventType_LeftButtonDown
              draw = 1
              
            Case #PB_EventType_LeftButtonUp              
              draw = 0
              
          EndSelect         
          
        Case #TB_BrushSize
          Brush\Size= GetGadgetState(#TB_BrushSize)
          
        Case #TB_BrushAlpha
          Brush\Alpha= GetGadgetState(#TB_BrushAlpha)
          Brush\Color = RGBA(Red(Color),Green(Color),Blue(color),Brush\Alpha)
          
        Case #Button_BrushColor
          Color = ColorRequester(Brush\Color)
          Brush\Color = RGBA(Red(Color),Green(Color),Blue(color),Brush\Alpha)
                    
      EndSelect
      
    Case #PB_Event_CloseWindow
      If WindowID=#win_0
        quit.b=1
      EndIf
            
  EndSelect
Until quit
;}


note : pour comparer l'utilisation de petites dalles avec des grosses dalles, il faut changer le variable size.

[edit]
Optimisation :
- ne faire un update que les dalles touchées par la souris (ou proche)
[/edit]

Autres optimisations :
- ne faire un update que une image (imagefinale) de la taille de la surface de dessin visible et non du document, en fonction d'un zoom éventuel

Si vous avez des idées d'optimisation possibles, vous pouvez aussi les poster bien sûr.
Dernière modification par blendman le jeu. 29/août/2013 18:17, modifié 1 fois.
Avatar de l’utilisateur
falsam
Messages : 7244
Inscription : dim. 22/août/2010 15:24
Localisation : IDF (Yvelines)
Contact :

Re: [5.20b] Système de dalle pour drawing 2D

Message par falsam »

Mais quel feignasse ce mec !!! :mrgreen: Tu pourrais mettre un titre à tes potentiomètres pour qu'on sache que le premier regle la taille du crayon et le second la transparence hein ? ha ha ha.

A part ça j'aime bien. Il me semble qu'il y a un bug quand on trace sans relâcher la souris sur plusieurs cases. Le tracé apparaît qu'en relâchant le clic de la souris. Peut être que c'est voulu.
Configuration : Windows 11 Famille 64-bit - PB 6.03 x64 - AMD Ryzen 7 - 16 GO RAM
Vidéo NVIDIA GeForce GTX 1650 Ti - Résolution 1920x1080 - Mise à l'échelle 125%
Avatar de l’utilisateur
blendman
Messages : 2017
Inscription : sam. 19/févr./2011 12:46

Re: [5.20b] Système de dalle pour drawing 2D

Message par blendman »

J'ai mis à jour le code :
- ajout de tooltip pour Falsam ^^ (c'était pas non plus le truc important, hein)
- optimisation du code pour l'affichage des dalles
- modification du code pour afficher le dessin en temps réel sur chaque dalle.

Il restera à trouver une technique optimisée permettant de connaitre les dalles "touchées" par la brosse.(soit quand elle est grande soit quand elle est au bord de plusieurs dalles en même temps).
Avatar de l’utilisateur
blendman
Messages : 2017
Inscription : sam. 19/févr./2011 12:46

Re: [5.30] Système de dalle pour drawing 2D

Message par blendman »

Salut

J'avais aussi réalisé ce code-ci (une autre technique), qui permet de dessiner sur LES dalles touchées, en prenant en compte le rayon de la brosse.
J'ai un autre code, qui permet d'afficher les dalles des calques au dessus de notre calques actifs, pour que ça s'update en temps réel, je le posterai lorsque je l'aurai un peu avancé.

Les optimisations sont les suivantes :
- je dessine sur le canvas pour l'affichage (on peut donc facilement dessiner sur du 5000*5000 :))
- je ne dessine que sur les dalles touchées (en prenant en compte le rayon de ma brosse)
- je ne mets à jour (lorsque je relève la souris) que les dalles touchées.

Il reste à faire :
- dessiner aussi les calques du dessus (ça, c'est ok dans mes tests).
- prendre en compte lorsqu'une dalle est "touchée" par la ligne formée en deux points (ça je ne sais pas comment faire ^^)

Code : Tout sélectionner

;{ infos
; test de dalle pour painting 2D
; animatoon
; dev :blendman 08/2011 - 06/2015
; pb  : 5.30 & 5.2x 
;}

;{ constantes
;window
Enumeration
  #Win_0
EndEnumeration

; menu
Enumeration 
  #menu_Sortie
EndEnumeration

; gadget
Enumeration
  #SA_surfaceDessin
  #canvas
EndEnumeration

; Image
Enumeration
  #image_dessinfinal
  #imageDessin  = 1000
EndEnumeration
;
;}

;{ Structures
Structure s_dalle
  id.a
  position.point
  image.w
  width.w
  height.w
  Touched.a
EndStructure
Global NewList dalle.s_dalle()

;}

;{ variables
Global doc_H.w=2000, doc_W.w =2000, draw.b, xx.w, yy.w, globalID.a
;}

;{ declare
Declare update_drawing()
;}

;{ openwindow
ExamineDesktops()
winW = DesktopWidth(0)
winH = DesktopHeight(0)

If OpenWindow(#Win_0, 0,0,winW,winH,"Animatoon-test dalle", #PB_Window_SystemMenu|#PB_Window_MinimizeGadget|#PB_Window_MaximizeGadget|#PB_Window_WindowCentered|#PB_Window_Maximize|#PB_Window_Minimize)

  ;{ Menus
  MenuBar=CreateMenu(#PB_Any,WindowID(#Win_0))
  MenuTitle("Fichier")
  MenuItem(#menu_Sortie,"Quitter")

  ;}
  ;{ Status barre
  StatusBar=CreateStatusBar(#PB_Any,WindowID(#Win_0))
  AddStatusBarField(200)
  AddStatusBarField(200)
  ;}
  ;{ Gadget & images
  ; creation des dalles (256*256), en fonction de la taille du document
  nb_dalle_w = Round(doc_w/256,0)
  nb_dalle_h = Round(doc_h/256,0) 
 
  For i = 0 To nb_dalle_w ; on commence les images à 1000, ça laisse de la marge au cas où on aurait besoin de 1000 images pour le logiciel
    For j = 0 To nb_dalle_h     
      ; creation of the image for the dalle
      CreateImage(#imageDessin+a, 256,256,32)
      StartDrawing(ImageOutput(#imageDessin+a))
      DrawingMode(#PB_2DDrawing_AlphaChannel)
      Box(0,0,256,256,RGBA(0,0,0,0))
      DrawingMode(#PB_2DDrawing_AlphaBlend)
      Box(0,0,256,256,RGBA(255-Random(50),255-Random(50),255-Random(50),255))
      StopDrawing()
      ; add the dalle on the list dalle()
      AddElement(dalle())
      dalle()\id = a 
      dalle()\position\y = j*256
      dalle()\position\x = i*256
      dalle()\width = 256
      dalle()\height = 256
      dalle()\image = #imageDessin+a
      a + 1
    Next j
  Next i
 
;   ; creation de l'image (support) pour le dessin
    CreateImage(#image_dessinfinal,doc_w,doc_h,32)
 
  ; scrollareagadget & canvas // surface de dessin
  ScrollAreaGadget(#SA_surfaceDessin,150,10,winW-150-150,winH-120,doc_W,doc_H,10)
  CanvasGadget(#canvas,0,0,doc_W,doc_H, #PB_Canvas_ClipMouse)
  CloseGadgetList()
  
  update_drawing()
 R = 30 ; rayon
  ;}
 
EndIf

;}

;{ Boucle
Repeat
  EventID  =WaitWindowEvent()
  MenuID   =EventMenu()
  GadgetID =EventGadget()
  WindowID =EventWindow()
 
  ;{ general
  XX = WindowMouseX(#Win_0)-150 + GetGadgetAttribute(#SA_surfaceDessin,#PB_ScrollArea_X)
  YY = WindowMouseY(#Win_0) -10 + GetGadgetAttribute(#SA_surfaceDessin,#PB_ScrollArea_Y)
  StatusBarText(StatusBar,0,"Coords : "+Str(XX)+","+Str(YY))
  ;}
 
  Select EventID
     
    Case #PB_Event_Menu
      Select MenuID
        Case #menu_Sortie
          quit.b=1
      EndSelect
     
    Case #PB_Event_Gadget
      Select GadgetID
         
        Case #canvas
          Select EventType()
            Case #PB_EventType_LeftButtonDown
              draw = 1
         
             
          EndSelect
         
        Case #SA_surfaceDessin
         
         
         
      EndSelect
   
      ;{ mouse & autres
    Case #PB_Event_CloseWindow
      If WindowID=#win_0
        quit.b=1
      EndIf
       
    Case #WM_MOUSEMOVE
      If draw = 1
        ForEach dalle()
          If XX>=dalle()\position\x-R And XX<=dalle()\position\x+dalle()\width+R And YY>=dalle()\position\y-R And YY<=dalle()\position\y+dalle()\height+R
            ;Debug "id : "+Str(dalle()\id)
            globalId = dalle()\id
            dalle()\Touched = 1
            If StartDrawing(ImageOutput(dalle()\image))
              Circle(XX-dalle()\position\x ,YY-dalle()\position\y,R,0)
              StopDrawing()
            EndIf
            ; update_drawing()
            ;Break
          EndIf
        Next
        If StartDrawing(CanvasOutput(#canvas))
          Circle(XX ,YY,R,0)
          StopDrawing()
        EndIf
      EndIf
     
    Case #WM_LBUTTONUP
      If draw = 1
        draw = 0
        ; Debug "Butoon_Up - Draw " +Str(draw)
        update_drawing()
      EndIf 
    ;} 
   
  EndSelect
Until quit
;}

End


Procedure  update_drawing()
 
;   StartDrawing(ImageOutput(#image_dessinfinal))
;   DrawingMode(#PB_2DDrawing_AlphaChannel)
;   Box(0,0,doc_W,doc_h,RGBA(0,0,0,0))
;   DrawingMode(#PB_2DDrawing_AlphaBlend)
;   ForEach dalle()   
;     DrawAlphaImage(ImageID(dalle()\image),dalle()\position\x,dalle()\position\y)   
;   Next 
;   StopDrawing()
 
  StartDrawing(CanvasOutput(#canvas))
  DrawingMode(#PB_2DDrawing_Default)
  ; Box(0,0,doc_w,doc_h,RGBA(255,255,255,255))
  ForEach dalle() 
    If dalle()\touched= 1
      dalle()\touched= 0
      DrawAlphaImage(ImageID(dalle()\image),dalle()\position\x,dalle()\position\y)
    EndIf
  Next 
  ;DrawAlphaImage(ImageID(#image_dessinfinal),0,0)
  StopDrawing()

EndProcedure
comtois
Messages : 5172
Inscription : mer. 21/janv./2004 17:48
Contact :

Re: [5.30] Système de dalle pour drawing 2D

Message par comtois »

blendman a écrit : - prendre en compte lorsqu'une dalle est "touchée" par la ligne formée en deux points (ça je ne sais pas comment faire ^^)
Cet algo devrait t'aider

https://en.wikipedia.org/wiki/Cohen%E2% ... _algorithm
http://purebasic.developpez.com/
Je ne réponds à aucune question technique en PV, utilisez le forum, il est fait pour ça, et la réponse peut profiter à tous.
Répondre