Dans mon logiciel animatoon (painting 2D), je vais ajouter un module animation, pour faire de l'animation 2D (bitmap, et sans doute vectorielle).
J'ai donc commencé à en développer un.
C'est très très loin d'être terminé, mais je vous poste le début du code, si vous avez envie de tester un peu ça

(je dois corriger des choses, et ajouter plein de fonctions, puis l'intégrer dans animatoon).
Un petit screenshot :

Le code :
Code : Tout sélectionner
; 2D Animation programm
; blendman July 2021
;{ changes
; 2.7.2021 0.2
; - add stroke, ognion skinning, export (all images), bugfixes
; 1.7.2021 0.1
; - we can play animation
; - When draw, if not keyframe, it add image
; - Canvas draw
; - gadgets timeline (addlayeranim, addkeyframe, frame start, end, current, fps, speed, button play/stop)
; - timeline (keyframe, start, end)
;}
; init the image lib
UsePNGImageDecoder()
UsePNGImageEncoder()
UseJPEGImageDecoder()
UseJPEGImageEncoder()
Enumeration
#G_canvasDrawing=0
#G_cont_CanvasDrawing
#G_ToolColor
#G_ToolSize
#G_ToolAlpha
#G_ToolImage
#G_ToolType ; brush=0, eraser=1
#G_cont_Timeline
#G_AddLayerAnim
#G_DelLayerAnim
#G_AddFrame
#G_DelFrame
#G_OgnionPrevious
#G_OgnionNext
#G_FrameStart
#G_FrameEnd
#G_FrameCurrent
#G_FrameSpeed
#G_FrameFPS
#G_PlayAnim
#G_StopAnim
#G_TimeLine
; menu
#menu_FileOpen=0
#menu_FileSave
#menu_FileExport
#Menu_LayerClear
#Menu_LayerImportImage
#Menu_Edit_CopyKeyframe
#Menu_Edit_Pastekeyframe
EndEnumeration
Structure sImageFrame
x.w
y.w
Frame.w
image.i
EndStructure
Structure sLayerAnim
Array Image.sImageFrame(0)
position.w ; position in Y, in the layeranima list
visible.a
lock.a
alpha.a
selected.a
color.i
type.a
name$
EndStructure
Global Dim LayerAnim.sLayerAnim(0)
Global NbFrame=-1, NblayerAnim=-1, NBlayerTotal, ImageCurrent, LayerAnimID
Structure sAnimationParameters
FPS.w
Speed.f
FrameCurrent.w
FrameStart.w
FrameEnd.w
OgnionPrevious.a
OgnionNext.a
EndStructure
Global Anim.sAnimationParameters
With Anim
\fps = 12
\Speed=1.0
\FrameCurrent=0
\FrameStart=0
\FrameEnd=12
EndWith
Structure sPoint
x.f : y.f
EndStructure
Structure sStroke
Array Dot.sPoint(0)
EndStructure
Global Dim Stroke.sStroke(0), StrokeID, nbStroke=-1, DotCurrent, nbdot=0
Global previousx, previousy
; distance, direction..
Macro point_distance(x1,y1,x2,y2)
Int(Sqr((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1)) )
EndMacro
Macro point_direction(x1,y1,x2,y2)
ATan2((y2- y1),(x2- x1))
EndMacro
Procedure AddSpinGadget(gad,x,y,w,h,min,max,tip$=#Empty$, val=0)
SpinGadget(gad,x,y,w,h,min,max,#PB_Spin_Numeric)
SetGadgetState(gad,val)
GadgetToolTip(gad,tip$)
EndProcedure
Procedure AddButtonGadget(gad,x,y,w,h,text$,tip$=#Empty$)
ButtonGadget(gad,x,y,w,h,text$)
GadgetToolTip(gad,tip$)
EndProcedure
Procedure AddKeyFrame()
;NbFrame+1
;Frame.sFrame(0)
EndProcedure
Procedure LayerAnimAdd()
NblayerAnim+1
i = NblayerAnim
NBlayerTotal+1
ReDim LayerAnim.sLayerAnim(i)
With LayerAnim(i)
\name$="Layer"+NBlayerTotal
\position = i
EndWith
EndProcedure
Procedure PaintbrushLine(x1,y1, x2,y2)
thesize = 10
pas.d = 30*0.01
; the distancebetween Two dots
distBetween2dot = Thesize*Pas
;distance between two vectors
Distance.d = point_distance(x1,y1, x2,y2)
; number of dots to draw
CountPoint = Distance/(Pas*Thesize)
; color
r = 0
g = 0
b = 0
a = 150
; Draw
DrawingMode(#PB_2DDrawing_AlphaBlend)
If distBetween2dot <= distance And CountPoint > 0
direction.d = point_direction(x1, y1, x2, y2)
For N = 0 To CountPoint
; then draw the dots
x3 = x1 + n * distBetween2dot * Sin(direction)
y3 = y1 + n * distBetween2dot * Cos(direction)
Circle(x3,y3,thesize/2,RGBA(0,0,0,a))
Next
previousx = x3
previousy = y3
Else
Circle(x1,y1,thesize/2,RGBA(0,0,0,a))
EndIf
EndProcedure
Procedure AddDot(x1,y1,size = 0)
i = nbdot
If i >0
xx = x1
yy = y1
StartX = Stroke(StrokeID)\Dot(i-1)\x
StartY = Stroke(StrokeID)\Dot(i-1)\y
dist = point_distance(xx,yy,StartX,StartY)
If dist > size
ok = 1
EndIf
Else
ok = 1
EndIf
If ok
ReDim Stroke(StrokeID)\Dot(nbdot)
Stroke(StrokeID)\Dot(nbdot)\x = x1
Stroke(StrokeID)\Dot(nbdot)\y = y1
nbdot+1
EndIf
EndProcedure
Procedure UpdateCanvas(draw=0)
Static lastImage, lastlayer
i = LayerAnimID
j = anim\FrameCurrent
w=GadgetWidth(#G_canvasDrawing)
h=GadgetHeight(#G_canvasDrawing)
If j > ArraySize(LayerAnim(i)\Image())
ReDim LayerAnim(i)\Image(j)
EndIf
If draw = 1
If Not IsImage(LayerAnim(i)\Image(j)\image)
LayerAnim(i)\Image(j)\image = CreateImage(#PB_Any, w,h,32,#PB_Image_Transparent)
EndIf
EndIf
img = LayerAnim(i)\Image(j)\image
If draw
If ArraySize(Stroke(StrokeID)\Dot())>DotCurrent+1
If StartDrawing(ImageOutput(img))
DrawingMode(#PB_2DDrawing_AlphaBlend)
If ArraySize(Stroke(StrokeID)\Dot())=0
PaintbrushLine(Stroke(StrokeID)\Dot(0)\x,Stroke(StrokeID)\Dot(0)\y,Stroke(StrokeID)\Dot(0)\x,Stroke(StrokeID)\Dot(0)\y)
Else
For i=DotCurrent To ArraySize(Stroke(StrokeID)\Dot())-1
;For i=0 To ArraySize(Stroke(StrokeID)\Dot())-1
PaintbrushLine(Stroke(StrokeID)\Dot(i+1)\x,Stroke(StrokeID)\Dot(i+1)\y,Stroke(StrokeID)\Dot(i)\x,Stroke(StrokeID)\Dot(i)\y)
Next
EndIf
DotCurrent = ArraySize(Stroke(StrokeID)\Dot())
StopDrawing()
EndIf
EndIf
EndIf
; update the canvas
If StartDrawing(CanvasOutput(#G_canvasDrawing))
Box(0,0,OutputWidth(), OutputHeight(), RGB(255,255,255))
DrawingMode(#PB_2DDrawing_AlphaBlend)
; ognion skining previous
k = LayerAnimID
j = anim\FrameCurrent
If (j-anim\OgnionPrevious)>=0
For i =(j-anim\OgnionPrevious) To j
If ArraySize(LayerAnim(k)\Image())>i
img2=LayerAnim(k)\Image(i)\image
If img2>0
a = 100
DrawAlphaImage(ImageID(img2),0,0, a)
EndIf
EndIf
Next
EndIf
; draw the layeranim image
img = LayerAnim(lastlayer)\Image(lastImage)\image
For i=0 To ArraySize(LayerAnim())
If ArraySize(LayerAnim(i)\Image())>=J
img2 = LayerAnim(i)\Image(j)\image
If IsImage(img2)
DrawAlphaImage(ImageID(img2),0,0)
img=img2
lastlayer = i
lastImage = j
Else
If IsImage(img)
DrawAlphaImage(ImageID(img),0,0)
EndIf
EndIf
Else
DrawAlphaImage(ImageID(img),0,0)
EndIf
Next
; ognion skining next
For i =j To (j+anim\OgnionPrevious)
Next
StopDrawing()
EndIf
EndProcedure
Procedure UpdateTimeLine()
; size of a layer and keyframe
w = 20
h = 18
w1=200
w2=10
If StartDrawing(CanvasOutput(#G_TimeLine))
Box(0,0,OutputWidth(),OutputHeight(),RGB(100,100,100))
; DrawThe layer Animation
For i=0 To ArraySize(LayerAnim())
DrawingMode(#PB_2DDrawing_AllChannels)
With LayerAnim(i)
y = (2+h) * \position
c= 120
Box(0,y, OutputWidth(), h, RGBA(c,c,c,255))
c= 130
Box(0,y, w1, h, RGBA(c,c,c,255))
If LayerAnimID=i
c=120
Box(0,y, OutputWidth(), h, RGBA(c+20,c+10,c,255))
c=160
Box(0,y, w1, h, RGBA(c,c,c,255))
EndIf
DrawingMode(#PB_2DDrawing_Transparent)
DrawText(40,y,\name$)
EndWith
Next
DrawingMode(#PB_2DDrawing_AlphaBlend)
; draw the separation
Box(w1,0,w2,OutputHeight(),RGBA(200,200,200,255))
; Draw the keyframe
x1 = w1+w2+2
w3 =OutputWidth() - w1-w2
For i=0 To w3/w
LineXY(x1+i*w,0,x1+i*w,OutputHeight(),RGBA(255,255,255,180))
Next
; Draw the utilities for anim (start /end cursor, current frame cursor)
Box(x1+(Anim\FrameCurrent)*w,0,w,OutputHeight(),RGBA(200,150,150,140))
Box(x1+(Anim\FrameStart)*w,0,2,OutputHeight(),RGBA(0,255,255,255))
Box(x1+(Anim\FrameEnd+1)*w,0,2,OutputHeight(),RGBA(255,0,0,255))
StopDrawing()
EndIf
UpdateCanvas()
EndProcedure
Procedure EventTimeLine()
If EventType() = #PB_EventType_LeftButtonDown Or (EventType() = #PB_EventType_MouseMove And GetGadgetAttribute(#G_TimeLine, #PB_Canvas_Buttons) & #PB_Canvas_LeftButton)
x = GetGadgetAttribute(#G_TimeLine, #PB_Canvas_MouseX)
y = GetGadgetAttribute(#G_TimeLine, #PB_Canvas_MouseY)
If x < 200
LayerAnimID = y/18
If LayerAnimID> ArraySize(LayerAnim())
LayerAnimID= ArraySize(LayerAnim())
EndIf
Else
x = Round((x-215)/20,#PB_Round_Down)
If x <0
x=0
EndIf
Anim\FrameCurrent = x
SetGadgetState(#G_FrameCurrent, Anim\FrameCurrent)
EndIf
UpdateTimeLine()
EndIf
EndProcedure
If ExamineDesktops()
w=DesktopWidth(0)
h=DesktopHeight(0)
EndIf
If OpenWindow(0, 0, 0, w, h, "Animatoon - animation module", #PB_Window_SystemMenu|#PB_Window_MaximizeGadget|#PB_Window_Maximize|#PB_Window_MinimizeGadget)
ProjectName$ = "Project"+FormatDate("%dd%mm%yyyy%hh%ii%ss", Date())
;{ menu, gadgets
CreateMenu(0,WindowID(0))
MenuTitle("File")
MenuItem(#menu_FileOpen, "Open")
MenuItem(#menu_FileSave, "Save")
MenuItem(#menu_FileExport, "Export")
MenuTitle("Layer")
MenuItem(#Menu_LayerClear, "Clear Layer")
w = 800
h = 500
x=10
h1=200
c = 90 ; color theme
If ContainerGadget(#G_cont_CanvasDrawing,0,0,WindowWidth(0),WindowHeight(0)-200-MenuHeight())
SetGadgetColor(#G_cont_CanvasDrawing,#PB_Gadget_BackColor, RGB(c,c,c))
CanvasGadget(#G_canvasDrawing, x+WindowWidth(0)/2-w/2, 10, w, h)
CloseGadgetList()
EndIf
y=h-h1+25-MenuHeight()
w=30
h=25
d=30
c = 150 ; color theme
If ContainerGadget(#G_cont_Timeline,0,WindowHeight(0)-200-MenuHeight(),WindowWidth(0),h1)
SetGadgetColor(#G_cont_Timeline,#PB_Gadget_BackColor, RGB(c,c,c))
x=10
y=10
AddButtonGadget(#G_AddLayerAnim,x,y,w,h,"+","Add a layerAnim") : x+w+5
AddButtonGadget(#G_DelLayerAnim,x,y,w,h,"-","Delete the current layerAnim") : x+w+135
AddButtonGadget(#G_AddFrame,x,y,w,h,"+","Add keyframe (and image on the selected layer)") : x+w+5
AddButtonGadget(#G_DelFrame,x,y,w,h,"-","Delete the current keyframe (and delete all images for this keyframe on the layer)") : x+w+d
w=60
AddSpinGadget(#G_OgnionPrevious,x,y,w,h,0,5,"Ognion Skinning Previous",Anim\OgnionPrevious) : x+w+5
AddSpinGadget(#G_OgnionNext,x,y,w,h,0,5,"Ognion Skinning Next",Anim\OgnionNext) : x+w+d
AddSpinGadget(#G_FrameStart,x,y,w,h,0,1000,"Start of animation",Anim\FrameStart) : x+w+5
AddSpinGadget(#G_FrameEnd,x,y,w,h,1,1000,"End of animation",Anim\FrameEnd) : x+w+5
AddSpinGadget(#G_FrameCurrent,x,y,w,h,0,1000, "Current frame",Anim\FrameCurrent) : x+w+d
AddSpinGadget(#G_FrameSpeed,x,y,w,h,0,100, "Speed of animation",Anim\Speed*10) : x+w+5
AddSpinGadget(#G_FrameFPS,x,y,w,h,0,100, "FPS of animation",Anim\FPS) : x+w+d
w= 70
ButtonGadget(#G_PlayAnim,x,y,w,h,"Play",#PB_Button_Toggle) : x+w+5
ButtonGadget(#G_StopAnim,x,y,w,h,"Stop") : x+w+d
x=10
y+h+5
CanvasGadget(#G_TimeLine, 10, y, WindowWidth(0)-20, h1-60,#PB_Canvas_Border )
CloseGadgetList()
EndIf
LayerAnimAdd()
UpdateTimeLine()
;}
Repeat
Event = WaitWindowEvent(1)
EventGadget = EventGadget()
EventMenu = EventMenu()
If Event = #PB_Event_Menu
If EventMenu = #Menu_LayerClear
If ArraySize(LayerAnim(LayerAnimID)\Image())>=anim\FrameCurrent
If IsImage(LayerAnim(LayerAnimID)\Image(anim\FrameCurrent)\image)
If StartDrawing(ImageOutput(LayerAnim(LayerAnimID)\Image(anim\FrameCurrent)\image))
DrawingMode(#PB_2DDrawing_AllChannels)
Box(0,0,OutputWidth(), OutputHeight(), RGBA(0,0,0,0))
StopDrawing() : EndIf
updateCanvas(1)
EndIf
EndIf
ElseIf EventMenu = #menu_FileExport
oldlayerANimId = LayerAnimID
For k=0 To ArraySize(LayerAnim())
For i=0 To ArraySize(LayerAnim(k)\Image())
img = LayerAnim(k)\Image(i)\image
If IsImage(img)
If SaveImage(img, GetCurrentDirectory()+ProjectName$+"_Layer"+Str(k)+"_Image"+Str(i)+".png",#PB_ImagePlugin_PNG)
EndIf
EndIf
Next
Next
LayerAnimID= oldlayerANimId
EndIf
ElseIf Event = #PB_Event_Gadget
Select EventGadget
Case #G_OgnionPrevious,#G_OgnionNext
Anim\OgnionPrevious = GetGadgetState(#G_OgnionPrevious)
Anim\OgnionNext = GetGadgetState(#G_OgnionNext)
updateCanvas(0)
Case #G_AddLayerAnim
LayerAnimAdd()
UpdateTimeLine()
Case #G_FrameStart
Anim\FrameStart = GetGadgetState(EventGadget)
UpdateTimeLine()
Case #G_FrameEnd
Anim\FrameEnd = GetGadgetState(EventGadget)
UpdateTimeLine()
Case #G_FrameFPS, #G_FrameSpeed
Anim\FPS = GetGadgetState(#G_FrameFPS)
Anim\Speed = GetGadgetState(#G_FrameSpeed)/10
UpdateTimeLine()
Case #G_PlayAnim
PlayANim=1
animtimer = ElapsedMilliseconds()
Case #G_StopAnim
PlayANim=0
SetGadgetState(#G_PlayAnim,0)
Case #G_FrameCurrent
Anim\FrameCurrent = GetGadgetState(EventGadget)
UpdateTimeLine()
Case #G_TimeLine
EventTimeLine()
Case #G_canvasDrawing
If EventType() = #PB_EventType_LeftButtonUp
; StrokeID+1
ReDim Stroke.sStroke(StrokeID)
DotCurrent=0
nbdot=0
ElseIf EventType() = #PB_EventType_LeftButtonDown
x = GetGadgetAttribute(#G_canvasDrawing, #PB_Canvas_MouseX)
y = GetGadgetAttribute(#G_canvasDrawing, #PB_Canvas_MouseY)
AddDot(x,y)
previousx = x
previousY = y
updateCanvas(1)
ElseIf (EventType() = #PB_EventType_MouseMove And GetGadgetAttribute(#G_canvasDrawing, #PB_Canvas_Buttons) & #PB_Canvas_LeftButton)
x = GetGadgetAttribute(#G_canvasDrawing, #PB_Canvas_MouseX)
y = GetGadgetAttribute(#G_canvasDrawing, #PB_Canvas_MouseY)
AddDot(x,y)
previousx = x
previousY = y
updateCanvas(1)
EndIf
EndSelect
EndIf
If PlayAnim = 1
If ElapsedMilliseconds() >animtimer + (1000/(anim\FPS * anim\Speed))
animtimer = ElapsedMilliseconds()
Anim\FrameCurrent +1
If Anim\FrameCurrent> Anim\FrameEnd
Anim\FrameCurrent= Anim\FrameStart
EndIf
UpdateTimeLine()
SetGadgetState(#G_FrameCurrent, Anim\FrameCurrent)
EndIf
EndIf
Until Event = #PB_Event_CloseWindow
EndIf
