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