Opération sur les Fonts

Programmation d'applications complexes
Avatar de l’utilisateur
blendman
Messages : 2017
Inscription : sam. 19/févr./2011 12:46

Re: Opération sur les Fonts

Message par blendman »

pour l'ombre portée, j'avais tenté ça, mais il y a un bug purebasic avec le resizeImage() qui crée un bord blanc quand on agrandit/rétrécit une image.

Edit : j'ai corrigé le code en copiant l'image du flou sur une seconde image et c'est cette image que je colle sur l'image finale ^^.

Code : Tout sélectionner

; Date: 22/03/2014
; blendman
; font effect

#width = 700
#height = 300


Structure sFontFx
  
  x.w
  y.w
  blur.a
  opacite.a
  
EndStructure
Global font.sFontFx

font\blur = 5
font\opacite = 255
font\x = 20
font\y = 70


Enumeration 
  
  #G_Image
  #G_OpacityShadow
  #G_BlurShadowPrecision
  
EndEnumeration

Procedure FontFX()
  
  temp = CreateImage(#PB_Any, #width, (#height-50),32, #PB_Image_Transparent)
  
  If StartDrawing(ImageOutput(temp))     
    DrawingMode(#PB_2DDrawing_AlphaChannel)    
    Box(0,0,#width,#height, RGBA(0,0,0,0))  ; canal alpha vide
    DrawingFont(FontID(0)) 
    DrawingMode(#PB_2DDrawing_AlphaBlend |#PB_2DDrawing_Transparent)
    DrawText(font\x, font\y,"PureBasic - Bold",RGBA(0,0,0,255))
    StopDrawing()
  EndIf
  
  ResizeImage(temp, #width / font\Blur, (#height-50)/font\Blur) ; bug contour blanc !
  ResizeImage(temp, #width , (#height-50))
  
  temp2 = CreateImage(#PB_Any, #width, (#height-50),32)
  If StartDrawing(ImageOutput(temp2))
    
    Box(0,0,#width,#height, RGBA(0,0,0,255))
    DrawingMode(#PB_2DDrawing_AlphaChannel)
    DrawAlphaImage(ImageID(temp), 0, 0, font\opacite)
    
    StopDrawing()
  EndIf
  
  
  If StartDrawing(ImageOutput(1))
    Box(0,0,#width,#height, RGBA(80,80,80,255))  ; fond gris
    
    ; effet shadow   
    DrawAlphaImage(ImageID(temp2), 0, 0, font\opacite)
    
    DrawingMode(#PB_2DDrawing_AlphaBlend |#PB_2DDrawing_Transparent)
    DrawingFont(FontID(0)) 
    
    DrawText(11,61,"PureBasic - Bold",RGBA(50,120,120,255))
    
    StopDrawing()
  EndIf
  
  SetGadgetState(#G_Image, ImageID(1))
  FreeImage(temp)
  FreeImage(temp2)
  
EndProcedure


If OpenWindow(1,100,200,#width,#height,"Fonts Effects",#PB_Window_SystemMenu)
  
  
  TrackBarGadget(#G_OpacityShadow, 0, #height - 40, 100,20,0,255)
  GadgetToolTip(#G_OpacityShadow,"shadow opacity")
  SetGadgetState(#G_OpacityShadow, font\opacite)
  
  SpinGadget(#G_BlurShadowPrecision, 110, #height - 40, 50,20,1,20, #PB_Spin_Numeric)
  GadgetToolTip(#G_BlurShadowPrecision,"shadow precision")
  SetGadgetState(#G_BlurShadowPrecision, font\blur)
  
  LoadFont(0, "Verdana",48,#PB_Font_Bold|#PB_Font_HighQuality)
  
  If CreateImage(1,#width,#height-50,32)    
    ImageGadget(#G_Image,0,0,#width ,#height -50, ImageID(1))
  EndIf
  FontFX()
  
  
  Repeat
    event = WaitWindowEvent()
    
    
    Select event
        
      Case #PB_Event_Gadget
        
        Select EventGadget()
            
          Case #G_OpacityShadow
            font\opacite = GetGadgetState(#G_OpacityShadow)
            FontFX()
            
          Case #G_BlurShadowPrecision
            font\blur = GetGadgetState(#G_BlurShadowPrecision)
            FontFX()
            
        EndSelect        
        
    EndSelect
        
  Until  event = #PB_Event_CloseWindow
EndIf
  
Avatar de l’utilisateur
graph100
Messages : 1318
Inscription : sam. 21/mai/2005 17:50

Re: Opération sur les Fonts

Message par graph100 »

Cool :D Ca marche vraiment bien !
_________________________________________________
Mon site : CeriseCode (Attention Chantier perpétuel ;))
Avatar de l’utilisateur
graph100
Messages : 1318
Inscription : sam. 21/mai/2005 17:50

Re: Opération sur les Fonts

Message par graph100 »

Bon c'est vraiment long, mais voila un petit aperçu de ce que je voulais faire à la base.
Le code fait
- inscription du texte dans une bulle
- rotation du texte dans la bulle
- contour du texte
- couleur différente pour le texte et le contour

Il reste à faire :
- découpage des mots lorsqu'ils sont trop grand (mettre un tiret etc..) entre les syllabes
- un bug lors de la présence d'un contour, qui fait que le texte n'est pas dessiné avec la bonne couleur, mais en composition avec la couleur du contour...
- diverses artéfact sur le tour du contour qui restent à corriger.
- Bug bizarre très rare qui fait qu'une seule ligne (de pixel) sur 2 est dessinée.. (vraiment chelou celui-la)

Quelqu'un a une idée pour atténuer le lissage du à l'antialiasing du texte ?
[édit] Le dernier trackbar sert à modifier l'antialiasing.

Code : Tout sélectionner

DisableDebugger

;{ MODULE Font

;{ Font Style
CompilerIf  #PB_Compiler_OS <> #PB_OS_Windows
	#BF_Font_Normal = 0
	#BF_Font_Italic = 2
	#BF_Font_Bold = 1
	#BF_Font_BoldItalic = 3
CompilerEndIf

CompilerIf #PB_Compiler_OS = #PB_OS_Windows
	#BF_Font_Normal = 0
	#BF_Font_Italic = 512
	#BF_Font_Bold = 256
	#BF_Font_BoldItalic = 768
CompilerEndIf


;}

Structure IBF_Glyphe
	*Adresse_Font.IBF_Font
	
	char.s{1}
	
	Array Image.a(0, 0)
	
	Largeur.w
	Hauteur.w
EndStructure

Structure IBF_Font
	FontId.i
	Font.l
	
	; Distance entre le bas du caractère "a" dans la police et le haut d'un glyphe dans cette police.
	; Sert à écrire toutes les tailles de police sur la même ligne (hypothétiquement !!!)
	BaseLine.l
	
	FontKey.s
	
	Size.w
	Style.l
	
	Map *Glyphe.IBF_Glyphe()
EndStructure


Structure IBF_RGBA
	r.a
	g.a
	b.a
	a.a
EndStructure

Structure IBF_Char
	*Glyphe.IBF_Glyphe
	
	StructureUnion
		couleur.l
		RGBA.IBF_RGBA
	EndStructureUnion
EndStructure

Structure IBF_Text
	List Text.IBF_Char()
EndStructure


Structure IBF_Var_Global
	Map Font.IBF_Font()
	
	Map Glyphe.IBF_Glyphe()
	
	ImageTestTextSize.l
EndStructure



;{ VAR Global

Global IBF_Global.IBF_Var_Global
InitializeStructure(@IBF_Global, IBF_Var_Global)

IBF_Global\ImageTestTextSize = CreateImage(#PB_Any, 1, 1)

;}

Procedure IBF_GetGrayArray(Array image.l(2), Array image_gray.a(2))
	
	Protected *adr.IBF_RGBA
	
	w = ArraySize(image(), 1)
	h = ArraySize(image(), 2)
	
	Dim image_gray(w, h)
	
	For x = 0 To w
		For y = 0 To h
			*adr = @image(x, y)
			tmp.d = *adr\r + *adr\g + *adr\b
			If tmp > 255 : tmp = 255 : EndIf
			
			image_gray(x, y) = tmp * (*adr\a / 255)
		Next
	Next
	
EndProcedure

Procedure IBF_GetEdgeGray(Array image.a(2), rayon = 1)
	w = ArraySize(image(), 1) - 1
	h = ArraySize(image(), 2) - 1
	
	Dim image_save.a(w + 1, h + 1)
	Dim image_tab.a(w + 1, h + 1)
	Dim image_tab2.a(w + 1, h + 1)
	
	Dim *adr.POINT(w + 1, h + 1)
	
	CopyArray(image(), image_tab2())
	
	NewList coord.POINT()
	NewList new_coord.POINT()
	
	Macro AjoutPoint(_x_, _y_)
		If *adr(_x_, _y_) = 0 And image_tab(_x_, _y_) = 0 And _x_ < w + 1 And _y_ < h + 1 And _x_ > 0 And _y_ > 0
			AddElement(new_coord())
			
			*adr(_x_, _y_) = @new_coord()
			new_coord()\x = _x_
			new_coord()\y = _y_
		EndIf
	EndMacro
	
	CopyArray(image(), image_tab())
	
	;{ init
	For x = 1 To w
		For y = 1 To h
			
			If image_tab(x, y) < 255
				deltaX_.l = (image_tab(x + 1, y - 1) + image_tab(x + 1, y) << 1 + image_tab(x + 1, y + 1)) - (image_tab(x - 1, y - 1)   + image_tab(x - 1, y) << 1 + image_tab(x - 1, y + 1))
				deltaY_.l = (image_tab(x - 1, y - 1)  + image_tab(x, y - 1) << 1    + image_tab(x + 1, y - 1)  ) - (image_tab(x - 1, y + 1) + image_tab(x, y + 1) << 1 + image_tab(x + 1, y + 1))
				
				deltaX.d = deltaX_ / 3
				deltaY.d = deltaY_ / 3
				
				gray.l = Sqr(deltaX * deltaX + deltaY * deltaY)
				
				If gray > 0 Or image_tab(x, y) > 0
					
					If image_tab(x, y) > 0
						gray = 255 - image_tab(x, y)
					ElseIf gray > 255
						gray = 255
					EndIf
					
					image_tab2(x, y) = gray
					
					;{ ajout des points futurs
					
					If *adr(x, y) <> 0
						ChangeCurrentElement(new_coord(), *adr(x, y))
						DeleteElement(new_coord())
						*adr(x, y) = -1
					EndIf
					
					AjoutPoint(x+1, y)
					AjoutPoint(x, y+1)
					AjoutPoint(x-1, y)
					AjoutPoint(x, y-1)
					
					AjoutPoint(x+1, y+1)
					AjoutPoint(x+1, y-1)
					AjoutPoint(x-1, y+1)
					AjoutPoint(x-1, y-1)
					
					;}
					
					If rayon > 1
						image_save(x, y) = 255-image_tab(x, y)
					Else
						image_save(x, y) = gray
					EndIf
					
					;Plot(x, y, RGB(image_save(x, y), image_save(x, y), image_save(x, y)))
				EndIf
			EndIf
		Next
	Next
	;}
	
	;{ boucle normale
	For i = 2 To rayon
		CopyList(new_coord(), coord())
		CopyArray(image_tab2(), image_tab())
		
		Dim *adr.POINT(w + 1, h + 1)
		
		ClearList(new_coord())
		
		ForEach coord()
			x = coord()\x
			y = coord()\y
			
			If image_save(x, y) < 255
				deltaX_.l = (image_tab(x + 1, y - 1) + image_tab(x + 1, y) << 1 + image_tab(x + 1, y + 1)) - (image_tab(x - 1, y - 1)   + image_tab(x - 1, y) << 1 + image_tab(x - 1, y + 1))
				deltaY_.l = (image_tab(x - 1, y - 1)  + image_tab(x, y - 1) << 1    + image_tab(x + 1, y - 1)  ) - (image_tab(x - 1, y + 1) + image_tab(x, y + 1) << 1 + image_tab(x + 1, y + 1))
				
				deltaX.d = deltaX_ / 3
				deltaY.d = deltaY_ / 3
				
				gray.l = Sqr(deltaX * deltaX + deltaY * deltaY)
				
				If gray > 0 Or image_save(x, y) > 0
					
					If image_tab(x, y) > 0
						gray = 255 - image(x, y)
					ElseIf gray > 255
						gray = 255
					EndIf
					
					image_tab2(x, y) = gray
					
					;{ ajout des points futurs
					
					If *adr(x, y) <> 0
						ChangeCurrentElement(new_coord(), *adr(x, y))
						DeleteElement(new_coord())
						*adr(x, y) = -1
					EndIf
					
					AjoutPoint(x+1, y)
					AjoutPoint(x, y+1)
					AjoutPoint(x-1, y)
					AjoutPoint(x, y-1)
					
					AjoutPoint(x+1, y+1)
					AjoutPoint(x+1, y-1)
					AjoutPoint(x-1, y+1)
					AjoutPoint(x-1, y-1)
					
					;}
					
					If i < rayon
						image_save(x, y) = 255
					Else
						image_save(x, y) = gray
					EndIf
					
					;Plot(x, y, RGB(image_save(x, y), image_save(x, y), image_save(x, y)))
					
				EndIf
			EndIf
		Next
	Next
	;}
	
	CopyArray(image_save(), image())
EndProcedure

Procedure IBF_CombineArray(Array image_IN_OUT.l(2), Array image_gray.a(2), *Couleur_Gray.IBF_RGBA)
	Protected *adr.IBF_RGBA
	
	w = ArraySize(image_IN_OUT(), 1)
	h = ArraySize(image_IN_OUT(), 2)
	
	For x = 0 To w
		For y = 0 To h
			If image_gray(x, y) > 0
				*adr = @image_IN_OUT(x, y)
				
				tmp.d = image_gray(x, y) / 255
				inv_tmp.d = 1 - tmp
				
				*adr\r = *adr\r * inv_tmp + *Couleur_Gray\r * tmp
				*adr\g = *adr\g * inv_tmp + *Couleur_Gray\g * tmp
				*adr\b = *adr\b * inv_tmp + *Couleur_Gray\b * tmp
				*adr\a = *adr\a * inv_tmp + *Couleur_Gray\a * tmp
			EndIf
		Next
	Next
EndProcedure

Procedure IBF_DrawRotatedArrayGray(Array image.a(2), centre_x, centre_y, x, y, Angle.d, couleur.l, Antialiasing = #True)
	Protected.d Angle_Cos, Angle_Sin
	Protected.l w, h, iXc1, iYc1, iXc2, iYc2, iXs, iYs
	
	x - 1
	y - 1
	
	red.d = Red(Couleur) / 255
	green.d = Green(Couleur) / 255
	blue.d = Blue(Couleur) / 255
	
	x_max.l = OutputWidth() - 1
	y_max.l = OutputHeight() - 1
	
	Angle_Cos = Cos(Angle)
	Angle_Sin = Sin(Angle)
	
	w_source = ArraySize(image(), 1) ;- 1
	h_source = ArraySize(image(), 2) ;- 1
	
	w = Int(w_source * Abs(Angle_Cos) + h_source * Abs(Angle_Sin))
	h = Int(h_source * Abs(Angle_Cos) + w_source * Abs(Angle_Sin))
	
	iXc1 = w_source >> 1
	iYc1 = h_source >> 1
	iXc2 = w >> 1
	iYc2 = h >> 1
	
	c.d = x + iXc1
	d.d = y + iYc1
	new_angle.d = ATan2(c, d) + Angle; * #PI / 180
	dist.d = Sqr(c * c + d * d)
	
	c = centre_x + dist * Cos(new_angle) - iXc2
	d = centre_y + dist * Sin(new_angle) - iYc2
	
	Select Antialiasing
		Case #False
			For iY = 0 To h - 1
				For iX = 0 To w - 1
					
					; For each nDestImage point find rotated nSrcImage source point
					iXs = iXc1 + (iX - iXc2) * Angle_Cos + (iY - iYc2) * Angle_Sin
					iYs = iYc1 + (iY - iYc2) * Angle_Cos - (iX - iXc2) * Angle_Sin
					
					If iXs >= 0 And iXs < w_source  And iYs >= 0 And iYs < h_source
						a = c + iX
						b = d + iY
						
						If a >= 0 And a < x_max And b >= 0 And b < y_max
							color.l = Point(a, b)
							tmp.a = 255 - image(iXs, iYs)
							
							; Plot(a, b, RGB(image(iXs, iYs) * red, image(iXs, iYs) * green, image(iXs, iYs) * blue))
							Plot(a, b, RGB(image(iXs, iYs) * red + tmp * Red(color) / 255, image(iXs, iYs) * green + tmp * Green(color) / 255, image(iXs, iYs) * blue + tmp * Blue(color) / 255))
						EndIf
					EndIf
				Next
			Next
			
		Case #True
			
			For iY = 0 To h - 1
				For iX = 0 To w - 1
					
					; For each nDestImage point find rotated nSrcImage source point
					fXs.d = iXc1 + (iX - iXc2) * Angle_Cos + (iY - iYc2) * Angle_Sin
					fYs.d = iYc1 + (iY - iYc2) * Angle_Cos - (iX - iXc2) * Angle_Sin
					
					; 					iXs0 = Int(fXs) ; Strange behaviour when pixel have color for fXs, fYs near 0
					; 					iYs0 = Int(fYs)
					iXs0 = Round(fXs, #PB_Round_Down)
					iYs0 = Round(fYs, #PB_Round_Down)
					
					If iXs0 >= 0 And iXs0 < w_source - 1  And iYs0 >= 0 And iYs0 < h_source - 1
						a = c + iX
						b = d + iY
						
						If a >= 0 And a < x_max And b >= 0 And b < y_max
							
							; Bottom left coords of bounding floating point rectangle on nSrcImage
							
							fXfs1.d = fXs - Int(fXs)
							fYfs1.d = fYs - Int(fYs)
							
							fXfs1less.d = 1 - fXfs1 - 0.000005 : If fXfs1less < 0 : fXfs1less = 0 : EndIf
							fYfs1less.d = 1 - fYfs1 - 0.000005 : If fYfs1less < 0 : fYfs1less = 0 : EndIf
							
							ic0.d = image(iXs0 + 1, iYs0) * fXfs1 + image(iXs0, iYs0) * fXfs1less
							ic1.d = image(iXs0 + 1, iYs0 + 1) * fXfs1 + image(iXs0, iYs0 + 1) * fXfs1less
							
							; Weight along axis Y
							ic = fYfs1less * ic0 + fYfs1 * ic1
							
							
							color.l = Point(a, b)
							tmp.a = 255 - ic
 							Plot(a, b, RGB(ic * red + tmp * Red(color) / 255, ic * green + tmp * Green(color) / 255, ic * blue + tmp * Blue(color) / 255))
						EndIf
					ElseIf iXs0 = w_source - 1  And iYs0 >= 0 And iYs0 < h_source - 1
						a = c + iX
						b = d + iY
						
						If a >= 0 And a < x_max And b >= 0 And b < y_max
							
							; Bottom left coords of bounding floating point rectangle on nSrcImage
							
							fXfs1.d = fXs - Int(fXs)
							fYfs1.d = fYs - Int(fYs)
							
							fXfs1less.d = 1 - fXfs1 - 0.000005 : If fXfs1less < 0 : fXfs1less = 0 : EndIf
							fYfs1less.d = 1 - fYfs1 - 0.000005 : If fYfs1less < 0 : fYfs1less = 0 : EndIf
							
							ic0.d = image(iXs0, iYs0) * fXfs1less
							ic1.d = image(iXs0, iYs0 + 1) * fXfs1less
							
							; Weight along axis Y
							ic = fYfs1less * ic0 + fYfs1 * ic1
							
							
							color.l = Point(a, b)
							tmp.a = 255 - ic
							Plot(a, b, RGB(ic * red + tmp * Red(color) / 255, ic * green + tmp * Green(color) / 255, ic * blue + tmp * Blue(color) / 255))
						EndIf
					ElseIf iXs0 >= 0 And iXs0 < w_source - 1 And iYs0 = h_source - 1
						a = c + iX
						b = d + iY
						
						If a >= 0 And a < x_max And b >= 0 And b < y_max
							
							; Bottom left coords of bounding floating point rectangle on nSrcImage
							
							fXfs1.d = fXs - Int(fXs)
							
							fXfs1less.d = 1 - fXfs1 - 0.000005 : If fXfs1less < 0 : fXfs1less = 0 : EndIf
							fYfs1less.d = 1 - fYfs1 - 0.000005 : If fYfs1less < 0 : fYfs1less = 0 : EndIf
							
							ic0.d = image(iXs0 + 1, iYs0) * fXfs1 + image(iXs0, iYs0) * fXfs1less

							
							; Weight along axis Y
							ic = fYfs1less * ic0
							
							
							color.l = Point(a, b)
							tmp.a = 255 - ic
							Plot(a, b, RGB(ic * red + tmp * Red(color) / 255, ic * green + tmp * Green(color) / 255, ic * blue + tmp * Blue(color) / 255))
						EndIf
					EndIf           
				Next
			Next
			
	EndSelect
	
	
EndProcedure

Procedure IBF_DrawRotatedArray(Array image.l(2), centre_x, centre_y, x, y, Angle.d, Antialiasing = #True, Coef_Sigmoide.d = 0)
	Protected.d Angle_Cos, Angle_Sin
	Protected.l w, h, iXc1, iYc1, iXc2, iYc2, iXs, iYs
	
	x - 1
	y - 1
	
	x_max.l = OutputWidth() - 1
	y_max.l = OutputHeight() - 1
	
	Angle_Cos = Cos(Angle)
	Angle_Sin = Sin(Angle)
	
	w_source = ArraySize(image(), 1) ;- 1
	h_source = ArraySize(image(), 2) ;- 1
	
	w = Int(w_source * Abs(Angle_Cos) + h_source * Abs(Angle_Sin))
	h = Int(h_source * Abs(Angle_Cos) + w_source * Abs(Angle_Sin))
	
	iXc1 = w_source >> 1
	iYc1 = h_source >> 1
	iXc2 = w >> 1
	iYc2 = h >> 1
	
	c.d = x + iXc1
	d.d = y + iYc1
	new_angle.d = ATan2(c, d) + Angle; * #PI / 180
	dist.d = Sqr(c * c + d * d)
	
	c = centre_x + dist * Cos(new_angle) - iXc2
	d = centre_y + dist * Sin(new_angle) - iYc2
	
	; DrawingMode(#PB_2DDrawing_AlphaBlend)
	
	Select Antialiasing
		;{ Pas d'Antialiasing
		Case #False
			For iY = 0 To h - 1
				For iX = 0 To w - 1
					
					; For each nDestImage point find rotated nSrcImage source point
					iXs = iXc1 + (iX - iXc2) * Angle_Cos + (iY - iYc2) * Angle_Sin
					iYs = iYc1 + (iY - iYc2) * Angle_Cos - (iX - iXc2) * Angle_Sin
					
					If iXs >= 0 And iXs < w_source  And iYs >= 0 And iYs < h_source
						a = c + iX
						b = d + iY
						
						If a >= 0 And a < x_max And b >= 0 And b < y_max And Alpha(image(iXs, iYs)) > 0
; 							Plot(a, b, image(iXs, iYs))
							
							; si on ne veux pas utiliser #PB_2DDrawing_AlphaBlend :
							color.l = Point(a, b)
							tmpa.d = Alpha(image(iXs, iYs)) / 255
							tmp_inv.d = 1 - tmpa
							
							Plot(a, b, RGB(Red(image(iXs, iYs)) * tmpa + Red(color) * tmp_inv, Green(image(iXs, iYs)) * tmpa + Green(color) * tmp_inv, Blue(image(iXs, iYs)) * tmpa + Blue(color) * tmp_inv))
						EndIf
					EndIf
				Next
			Next
			
			;}
			
		;{ AntiAliasing
		Case #True
			
			Macro Sigmoide(_x_)
				_x_ *(1-Coef_Sigmoide) + (Coef_Sigmoide) * ((1+TanH((-0.5+_x_) * Coef_Sigmoide * 10))/2)
			EndMacro
			
			
			For iY = 0 To h - 1
				For iX = 0 To w - 1
					
					; For each nDestImage point find rotated nSrcImage source point
					fXs.d = iXc1 + (iX - iXc2) * Angle_Cos + (iY - iYc2) * Angle_Sin
					fYs.d = iYc1 + (iY - iYc2) * Angle_Cos - (iX - iXc2) * Angle_Sin
					
					; 					iXs0 = Int(fXs) ; Strange behaviour when pixel have color for fXs, fYs near 0
					; 					iYs0 = Int(fYs)
					iXs0 = Round(fXs, #PB_Round_Down)
					iYs0 = Round(fYs, #PB_Round_Down)
					
					If iXs0 >= 0 And iXs0 < w_source - 1  And iYs0 >= 0 And iYs0 < h_source - 1
						a = c + iX
						b = d + iY
						
						If a >= 0 And a < x_max And b >= 0 And b < y_max
							
							; Bottom left coords of bounding floating point rectangle on nSrcImage
							
							fXfs1.d = fXs - Int(fXs)
							fYfs1.d = fYs - Int(fYs)
							
							fXfs1less.d = 1 - fXfs1 - 0.00005 : If fXfs1less < 0 : fXfs1less = 0 : EndIf
							fYfs1less.d = 1 - fYfs1 - 0.00005 : If fYfs1less < 0 : fYfs1less = 0 : EndIf
							
							If Coef_Sigmoide > 0
								fXfs1less = Sigmoide(fXfs1less)
								fYfs1less = Sigmoide(fYfs1less)
								fXfs1 = Sigmoide(fXfs1)
								fYfs1 = Sigmoide(fYfs1)
							EndIf
							
							icr = Red(image(iXs0, iYs0)) * fXfs1less
							icg = Green(image(iXs0, iYs0)) * fXfs1less
							icb = Blue(image(iXs0, iYs0)) * fXfs1less
							ica = Alpha(image(iXs0, iYs0)) * fXfs1less
							
							icr0 = Red(image(iXs0 + 1, iYs0)) * fXfs1 + icr
							icg0 = Green(image(iXs0 + 1, iYs0)) * fXfs1 + icg
							icb0 = Blue(image(iXs0 + 1, iYs0)) * fXfs1 + icb
							ica0 = Alpha(image(iXs0 + 1, iYs0)) * fXfs1 + ica
							
							icr = Red(image(iXs0, iYs0 + 1)) * fXfs1less
							icg = Green(image(iXs0, iYs0 + 1)) * fXfs1less
							icb = Blue(image(iXs0, iYs0 + 1)) * fXfs1less
							ica = Alpha(image(iXs0, iYs0 + 1)) * fXfs1less
							
							icr1 = Red(image(iXs0 + 1, iYs0 + 1)) * fXfs1 + icr
							icg1 = Green(image(iXs0 + 1, iYs0 + 1)) * fXfs1 + icg
							icb1 = Blue(image(iXs0 + 1, iYs0 + 1)) * fXfs1 + icb
							ica1 = Alpha(image(iXs0 + 1, iYs0 + 1)) * fXfs1 + ica
							
							
							; Plot(a, b, RGBA(fYfs1less * icr0 + fYfs1 * icr1, fYfs1less * icg0 + fYfs1 * icg1, fYfs1less * icb0 + fYfs1 * icb1, fYfs1less * ica0 + fYfs1 * ica1))
							
							; si on ne veux pas utiliser #PB_2DDrawing_AlphaBlend :
							color.l = Point(a, b)
							tmpa.d = (fYfs1less * ica0 + fYfs1 * ica1) / 255
							tmp_inv.d = 1 - tmpa
							
							Plot(a, b, RGB((fYfs1less * icr0 + fYfs1 * icr1) * tmpa + Red(color) * tmp_inv, (fYfs1less * icg0 + fYfs1 * icg1) * tmpa + Green(color) * tmp_inv, (fYfs1less * icb0 + fYfs1 * icb1) * tmpa + Blue(color) * tmp_inv))
						EndIf
					EndIf           
				Next
			Next
			
			;}
	EndSelect
	
	
EndProcedure



Procedure.i IBF_CreateGlyphe(*Font.IBF_Font, char.s)
	; This Function DOES not check if the specified Font really exist.
	
	Protected key$, *obj.IBF_Glyphe, w.w, h.w, x.w, y.w
	
	key$ = *Font\FontKey + "|" + char
	
	*obj = FindMapElement(IBF_Global\Glyphe(), key$)
	If *obj :	ProcedureReturn *obj : EndIf
	
	
	*obj = AddMapElement(IBF_Global\Glyphe(), key$)
	InitializeStructure(*obj, IBF_Glyphe)
	
	*obj\Adresse_Font = *Font
	*Font\Glyphe(char) = *obj
	
	*obj\char = char
	
	; Taille des caractères
	StartDrawing(ImageOutput(IBF_Global\ImageTestTextSize))
	DrawingFont(*Font\FontId)
	
	If char = #CR$ Or char = #LF$ ; Pour linux. Pour ces caractères on obtient une hauteur double, ce qui fait des lignes énormes
		w = TextWidth(" ")
		h = TextHeight(" ")
	Else
		w = TextWidth(*obj\char)
		h = TextHeight(*obj\char)
	EndIf
	
	*obj\Largeur = w
	*obj\Hauteur = h
	
	StopDrawing()
	
	Dim *obj\Image(w, h)
	
	
	If w <> 0 And h <> 0
		img = CreateImage(#PB_Any, w, h, 24)
		
		If img
			If StartDrawing(ImageOutput(img))
				DrawingFont(*Font\FontId)
				
				If char = #CR$ Or char = #LF$
					
				Else
					DrawText(0, 0, *obj\char, RGB(255, 255, 255), RGB(0, 0, 0))
				EndIf
				
; 				If char = " "
; 					For x = 0 To w - 1
; 						For y = 0 To h - 1
; 							*obj\Image(x, y) = 255
; 							
; 						Next
; 					Next
; 				Else
					For x = 0 To w - 1
						For y = 0 To h - 1
							color.l = Point(x, y)
							
							*obj\Image(x, y) = (Red(color) + Blue(color) + Green(color)) / 3
							
						Next
					Next
; 				EndIf
				
				StopDrawing()
			EndIf
			
; 			*obj\Image(0, 0) = 255
; 			*obj\Image(w, h) = 255
			
			FreeImage(img)
		EndIf
	EndIf
	
	ProcedureReturn *obj
EndProcedure

Procedure.i BF_LoadFont(FontName.s = "Arial", FontSize.l = 12, FontStyle.l = #BF_Font_Normal)
	Protected *font.IBF_Font, key$, *glyphe.IBF_Glyphe, x.w, y.w
	
	key$ = FontName + "|" + Str(FontSize) + "|" + Str(FontStyle)
	
	*font = FindMapElement(IBF_Global\Font(), key$)
	
	If *font : ProcedureReturn *font : EndIf
	
	
	*font = AddMapElement(IBF_Global\Font(), key$)
	InitializeStructure(*font, IBF_Font)
	
	*font\FontKey = key$
	
	*font\Font = LoadFont(#PB_Any, FontName, FontSize, FontStyle)
	*font\FontId = FontID(*font\Font)
	
	*font\Size = FontSize
	*font\Style = FontStyle
	
	
	; Détermination de \BaseLine
	CompilerSelect #PB_Compiler_OS
		CompilerCase #PB_OS_Windows
			*glyphe = IBF_CreateGlyphe(*font, Chr(12))
			
			If *glyphe
				x = 0
				y = *glyphe\Hauteur / 2
				
				While x < *glyphe\Largeur - 1 And *glyphe\Image(x, y) = 0
					x + 1
				Wend
				
				While y < *glyphe\Hauteur - 1 And *glyphe\Image(x, y) <> 0
					y + 1
				Wend
				
				
				*font\BaseLine = y - 1
				
			Else
				*font\BaseLine = *glyphe\Hauteur
			EndIf
			
		CompilerDefault
			*glyphe = IBF_CreateGlyphe(*obj, "a")
			
			If *glyphe
				
				x = 0
				y = *glyphe\Hauteur
				
				For y = *glyphe\Hauteur - 1 To 0 Step -1
					For x = 0 To *glyphe\Largeur - 1
						If *glyphe\Image(x, y) <> 0
							Break 2
						EndIf
					Next
				Next
				
				*font\BaseLine = y
				
			Else
				*font\BaseLine = *glyphe\Hauteur
			EndIf
			
	CompilerEndSelect
	
	
	ProcedureReturn *font
EndProcedure

Procedure.i BF_NewText(text.s, *Font.IBF_Font, couleur.l)
	*txt.IBF_Text = AllocateMemory(SizeOf(IBF_Text))
	InitializeStructure(*txt, IBF_Text)
	
	
	len = Len(text) - 1
	*adr = @text
	
	For c = 0 To len
		AddElement(*txt\Text())
		
		*txt\Text()\Glyphe = IBF_CreateGlyphe(*font, PeekS(*adr, 1))
		*txt\Text()\couleur = couleur
		
		*adr + SizeOf(Character)
	Next
	
	ProcedureReturn *txt
EndProcedure


;}


;{ MODULE bulle_text



Structure IBT_ADR_list_POINT
	List pourtour.POINT()
EndStructure

Structure BT_Bulle
	Centre.POINT
	
	List pourtour.POINT()
	
	USE_CattmullRom.b
	USE_Antialisating.b
	
	text.s
	angle_text.d ; radian
	
	*BF_Text.IBF_Text
	
	color.l
	
	*bulle_list.BT_Liste_bulle
EndStructure

Structure BT_Liste_bulle
	List bulle_list.BT_Bulle()
	
	IS_init.b
EndStructure



Procedure IBT_DrawRotatedText2(centre_x, centre_y, x, y, Texte$, Angle.d, Couleur) ; x et y sont relatifs au centre
	Protected new_angle.d = ATan2(x, y) - Angle * #PI / 180
	Protected dist.d = Sqr(x * x + y * y)
	
	DrawRotatedText(centre_x + dist * Cos(new_angle), centre_y + dist * Sin(new_angle), Texte$, Angle, Couleur)
	
; 	tx = centre_x + dist * Cos(new_angle)
; 	ty = centre_y + dist * Sin(new_angle)
; 	LineXY(tx, ty, tx + TextWidth(Texte$) * Cos(Radian(-Angle)), ty + TextWidth(Texte$) * Sin(Radian(-Angle)), RGB(255, 125, 0))
EndProcedure

Procedure IBT_CatmullRomSpline(t.f, *result.point, *p0.point, *p1.point, *p2.point, *p3.point)
	t2.f = t * t
	t3.f = t2 * t
	*result\x = (( 2.0 * *p1\x) +(- *p0\x + *p2\x) * t +(2.0 * *p0\x - 5.0 * *p1\x + 4 * *p2\x - *p3\x) * t2 +(- *p0\x + 3.0 * *p1\x - 3.0 * *p2\x + *p3\x) * t3)
	*result\x >> 1
	*result\y = (( 2.0 * *p1\y) +(- *p0\y + *p2\y) * t +(2.0 * *p0\y - 5.0 * *p1\y + 4 * *p2\y - *p3\y) * t2 +(- *p0\y + 3.0 * *p1\y - 3.0 * *p2\y + *p3\y) * t3)
	*result\y >> 1
EndProcedure



Procedure.i BT_InitBulleText(USE_CattmullRom.b)
	Static Bulle_list.BT_Liste_bulle
	
	If Bulle_list\IS_init = #False
		InitializeStructure(Bulle_map, BT_Liste_bulle)
		
		Bulle_list\IS_init = #True
	EndIf
	
	Protected *mem.BT_Bulle = AddElement(Bulle_list\bulle_list())
	
	If *mem
		InitializeStructure(*mem, BT_Bulle)
		
		*mem\bulle_list = @Bulle_list
		*mem\USE_CattmullRom = USE_CattmullRom
		*mem\USE_Antialisating = #True
		
		; Init l'emplacement du texte
		*mem\BF_Text = BF_NewText("", 0, 0)
		
	EndIf
	
	ProcedureReturn *mem
EndProcedure

Procedure BT_DrawBulleText(*bulle.BT_Bulle, zoom.f = 0.5)
	Protected angle.d, *cote.POINT 
	Protected NewList tour_incline.POINT()
	
	#AFFICHE_POINT = #False
	
	
	;{ Traitement du Cattmull
	
	Protected *adr_pourtour.IBT_ADR_list_POINT = *bulle + OffsetOf(BT_Bulle\pourtour)
	
	If *bulle\USE_CattmullRom
		Protected *p.POINT
		
		*adr_pourtour = AllocateMemory(SizeOf(IBT_ADR_list_POINT))
		InitializeStructure(*adr_pourtour, IBT_ADR_list_POINT)
		
		*p2.POINT = LastElement(*bulle\pourtour())
		*p1.POINT = PreviousElement(*bulle\pourtour())
		*p0.POINT = PreviousElement(*bulle\pourtour())
		
		
		ForEach *bulle\pourtour()
			*p3 = @*bulle\pourtour()
			
			tmp_x.l = *p1\x - *p2\x
			tmp_y.l = *p1\y - *p2\y
			div.l = Sqr(tmp_x * tmp_x + tmp_y * tmp_y) / 20
			
			For z = 1 To div
				;- CatmullRom
				*p = AddElement(*adr_pourtour\pourtour())
				
				IBT_CatmullRomSpline(z / div, *p, *p0, *p1, *p2, *p3)
				; Circle(*bulle\Centre\x + *p\x, *bulle\Centre\y + *p\y, 2, #Blue)
			Next
			
			*p0 = *p1
			*p1 = *p2
			*p2 = *p3
		Next
	EndIf
	
	;}
	
	;{ passage en coordonnées texte pivoté
	
	*adr_haut.POINT = 0 : haut = 100000000
	*adr_bas.point = 0 : bas = -100000000
	
	Protected *old.POINT = LastElement(*adr_pourtour\pourtour())
	
	ForEach *adr_pourtour\pourtour()
		angle.d = ATan2(*adr_pourtour\pourtour()\x, *adr_pourtour\pourtour()\y) - *bulle\angle_text
		dist.d = zoom * Sqr(*adr_pourtour\pourtour()\x * *adr_pourtour\pourtour()\x + *adr_pourtour\pourtour()\y * *adr_pourtour\pourtour()\y)
		
		*elem.POINT = AddElement(tour_incline())
		
		*elem\x = dist * Cos(angle)
		*elem\y = dist * Sin(angle)
		
		If *elem\y > bas
			bas = *elem\y
			*adr_bas = *elem
		EndIf
		
		If *elem\y < haut
			haut = *elem\y
			*adr_haut = *elem
			
		EndIf
		
		LineXY(*bulle\Centre\x + *old\x, *bulle\Centre\y + *old\y, *bulle\Centre\x + *adr_pourtour\pourtour()\x, *bulle\Centre\y + *adr_pourtour\pourtour()\y, #White)
		*old = *adr_pourtour\pourtour()
	Next
	
; 	ChangeCurrentElement(tour_incline(), *adr_haut)
; 	Circle(*bulle\Centre\x + *adr_haut\x, *bulle\Centre\y + *adr_haut\y, 3, #Red)
; 	DrawText(*bulle\Centre\x + *adr_haut\x + 5, *bulle\Centre\y + *adr_haut\y, Str(ListIndex(tour_incline())))
; 	
; 	Circle(*bulle\Centre\x + *adr_bas\x, *bulle\Centre\y + *adr_bas\y, 3, RGB(255, 125, 0))
	
	
	;}
	
	;{ liste des cotés
	
	NewList cote_gauche.POINT()
	NewList cote_droit.POINT()
	
	;{ test pour savoir de quel coté partir
	ChangeCurrentElement(tour_incline(), *adr_haut)
	*elem = PreviousElement(tour_incline())
	If *elem = 0 : *elem = LastElement(tour_incline()) : EndIf
	
	angle = ATan2(*elem\x - *adr_haut\x, *elem\y - *adr_haut\y)
	
	ChangeCurrentElement(tour_incline(), *adr_haut)
	*elem = NextElement(tour_incline())
	If *elem = 0 : *elem = FirstElement(tour_incline()) : EndIf
	
	If angle > ATan2(*elem\x - *adr_haut\x, *elem\y - *adr_haut\y)
		sens = -1
	Else
		sens = 1
	EndIf
	
	;}
	
	;{ parcours pour coté gauche
	; init
	*elem = *adr_haut : *cote = 0
	ChangeCurrentElement(tour_incline(), *adr_haut)
	
	Repeat
		If sens = 1
			*elem_suivant.POINT = NextElement(tour_incline())
			If *elem_suivant = 0 : *elem_suivant = FirstElement(tour_incline()) : EndIf
		Else
			*elem_suivant.POINT = PreviousElement(tour_incline())
			If *elem_suivant = 0 : *elem_suivant = LastElement(tour_incline()) : EndIf
		EndIf
		
		
		cst_y = *elem_suivant\y - *elem\y
		cst_x = *elem_suivant\x - *elem\x
		
		For i = *elem\y + 1 To *elem_suivant\y
			*cote = AddElement(cote_gauche())
			
			*cote\y = i
			*cote\x = ((i - *elem\y) * cst_x) / cst_y + *elem\x
			
			CompilerIf #AFFICHE_POINT
				Plot(*bulle\Centre\x + *cote\x, *bulle\Centre\y + *cote\y, #Blue)
			CompilerEndIf
		Next
		
		*elem = *elem_suivant
	Until *elem = *adr_bas
	
	
	;}
	
	;{ parcours pour coté droit
	; init
	*elem = *adr_haut : *cote = 0
	ChangeCurrentElement(tour_incline(), *adr_haut)
	sens = -sens
	
	Repeat
		If sens = 1
			*elem_suivant.POINT = NextElement(tour_incline())
			If *elem_suivant = 0 : *elem_suivant = FirstElement(tour_incline()) : EndIf
		Else
			*elem_suivant.POINT = PreviousElement(tour_incline())
			If *elem_suivant = 0 : *elem_suivant = LastElement(tour_incline()) : EndIf
		EndIf
		
		cst_y = *elem_suivant\y - *elem\y
		cst_x = *elem_suivant\x - *elem\x
		
		For i = *elem\y + 1 To *elem_suivant\y
			*cote = AddElement(cote_droit())
			
			*cote\y = i
			*cote\x = ((i - *elem\y) * cst_x) / cst_y + *elem\x
			
			CompilerIf #AFFICHE_POINT
				Plot(*bulle\Centre\x + *cote\x, *bulle\Centre\y + *cote\y, #Cyan)
			CompilerEndIf
		Next
		
		*elem = *elem_suivant
	Until *elem = *adr_bas
	
	
	;}
	
	If ListSize(cote_droit()) <> ListSize(cote_droit())
		Debug "D = " + ListSize(cote_droit()) + "   G = " + ListSize(cote_gauche())
	EndIf
	
	;{ correction d'une erreur de sens
	
	FirstElement(cote_droit())
	FirstElement(cote_gauche())
	
	If cote_droit()\x < cote_gauche()\x
		NewList tmp.POINT()
		CopyList(cote_droit(), tmp())
		CopyList(cote_gauche(), cote_droit())
		CopyList(tmp(), cote_gauche())
		
		FreeList(tmp())
	EndIf
	;}
	
	;}
	
	Select 2
		Case 1
	;{ dessin du texte - Standart
	
	*curseur_text.Character = @*bulle\text
	
	offset.POINT\x = 0
	offset\y = TextHeight("a")
	
	FirstElement(cote_gauche())
	FirstElement(cote_droit())
	Repeat
		
		t$ = Chr(*curseur_text\c)
		tw.l = TextWidth(t$)
		
		If cote_droit()\x - cote_gauche()\x > tw + offset\x And *curseur_text\c <> #CR
			IBT_DrawRotatedText2(*bulle\Centre\x, *bulle\Centre\y, cote_gauche()\x + offset\x, cote_gauche()\y, t$, -Degree(*bulle\angle_text), *bulle\color)
			
			offset\x + tw
			*curseur_text + SizeOf(Character)
		Else
			If *curseur_text\c = #CR
				*curseur_text + SizeOf(Character)
			EndIf
			
			offset\x = 0
			
			For i = 1 To offset\y
				res_g = NextElement(cote_gauche())
				res_d = NextElement(cote_droit())
				
				If res_d = 0 Or res_g = 0
					Break 2
				EndIf
			Next
		EndIf
		
	Until *curseur_text\c = 0
	;}
	
		Case 2
	;{ dessin du texte - pas de coupure des mots
	
	EndSelect
	
	*curseur_text.Character = @*bulle\text
	*curseur_text_end = @*bulle\text + Len(*bulle\text) * SizeOf(Character)
	
	offset.POINT\x = 0
	offset\y = TextHeight(" ")
	
	FirstElement(cote_gauche())
	FirstElement(cote_droit())
	
	DEBUT.b = #True
	
	
	Repeat
		*c.Character = *curseur_text
		
		Repeat
			*c + SizeOf(Character)
		Until *c\c = ' ' Or *c >= *curseur_text_end
		
		len = *c - *curseur_text
		t$ = PeekS(*curseur_text, len)
		
		tw.l = TextWidth(t$)
		
		If cote_droit()\x - cote_gauche()\x > tw + offset\x And *curseur_text\c <> #CR
			IBT_DrawRotatedText2(*bulle\Centre\x, *bulle\Centre\y, cote_gauche()\x + offset\x, cote_gauche()\y, t$, -Degree(*bulle\angle_text), *bulle\color)
			
			offset\x + tw
			*curseur_text = *c
			
			DEBUT = #False
		Else
			If *curseur_text\c = #CR
				*curseur_text + SizeOf(Character)
			EndIf
			
			offset\x = 0
			
			If DEBUT
				res_g = NextElement(cote_gauche())
				res_d = NextElement(cote_droit())
				
				If res_d = 0 Or res_g = 0
					Break 1
				EndIf
			Else
				
				For i = 1 To offset\y
					res_g = NextElement(cote_gauche())
					res_d = NextElement(cote_droit())
					
					If res_d = 0 Or res_g = 0
						Break 2
					EndIf
				Next
			EndIf
		EndIf
		
	Until *curseur_text >= *curseur_text_end
	;}
	
	;{ libération des ressources
	If *bulle\USE_CattmullRom
		FreeList(*adr_pourtour\pourtour())
		FreeMemory(*adr_pourtour)
	EndIf
	;}
	
EndProcedure


#BF_Text_VerticalCentered = 	%0000000000000001
#BF_Text_HorizontalCentered = %0000000000000010
#BF_Text_MotsEntiers = 				%0000000000000100


Procedure BT_DrawBulleText_(*bulle.BT_Bulle, zoom.f = 0.5, rayon.l = 3, Couleur_contour.l = 0, coef_sigmoide.d = 0, MiseEnForme.l = #BF_Text_HorizontalCentered)
	Protected angle.d, *cote.POINT 
	Protected NewList tour_incline.POINT()
	
	#AFFICHE_POINT = #False
	
	
	;{ Traitement du Cattmull
	
	Protected *adr_pourtour.IBT_ADR_list_POINT = *bulle + OffsetOf(BT_Bulle\pourtour)
	
	If *bulle\USE_CattmullRom
		Protected *p.POINT
		
		*adr_pourtour = AllocateMemory(SizeOf(IBT_ADR_list_POINT))
		InitializeStructure(*adr_pourtour, IBT_ADR_list_POINT)
		
		*p2.POINT = LastElement(*bulle\pourtour())
		*p1.POINT = PreviousElement(*bulle\pourtour())
		*p0.POINT = PreviousElement(*bulle\pourtour())
		
		
		ForEach *bulle\pourtour()
			*p3 = @*bulle\pourtour()
			
			tmp_x.l = *p1\x - *p2\x
			tmp_y.l = *p1\y - *p2\y
			div.l = Sqr(tmp_x * tmp_x + tmp_y * tmp_y) / 20
			
			For z = 1 To div
				;- CatmullRom
				*p = AddElement(*adr_pourtour\pourtour())
				
				IBT_CatmullRomSpline(z / div, *p, *p0, *p1, *p2, *p3)
				; Circle(*bulle\Centre\x + *p\x, *bulle\Centre\y + *p\y, 2, #Blue)
			Next
			
			*p0 = *p1
			*p1 = *p2
			*p2 = *p3
		Next
	EndIf
	
	;}
	
	;{ passage en coordonnées texte pivoté
	
	*adr_haut.POINT = 0
	*adr_bas.point = 0
	
	Protected *old.POINT = LastElement(*adr_pourtour\pourtour())
	
	Protected bulle_MAX.point, bulle_min.POINT
	
	bulle_min\x = $7FFFFFFF
	bulle_min\y = $7FFFFFFF
	bulle_MAX\x = -$7FFFFFFF
	bulle_MAX\y = -$7FFFFFFF
	
	ForEach *adr_pourtour\pourtour()
		angle.d = ATan2(*adr_pourtour\pourtour()\x, *adr_pourtour\pourtour()\y) - *bulle\angle_text
		dist.d = zoom * Sqr(*adr_pourtour\pourtour()\x * *adr_pourtour\pourtour()\x + *adr_pourtour\pourtour()\y * *adr_pourtour\pourtour()\y)
		
		*elem.POINT = AddElement(tour_incline())
		
		*elem\x = dist * Cos(angle)
		*elem\y = dist * Sin(angle)
		
		If *elem\y > bulle_MAX\y
			bulle_MAX\y = *elem\y
			*adr_bas = *elem
		EndIf
		
		If *elem\y < bulle_min\y
			bulle_min\y = *elem\y
			*adr_haut = *elem
		EndIf
		
		If *elem\x > bulle_MAX\x
			bulle_MAX\x = *elem\x
		EndIf
		
		If *elem\x < bulle_min\x
			bulle_min\x = *elem\x
		EndIf
		
		
		LineXY(*bulle\Centre\x + *old\x, *bulle\Centre\y + *old\y, *bulle\Centre\x + *adr_pourtour\pourtour()\x, *bulle\Centre\y + *adr_pourtour\pourtour()\y, #White)
		*old = *adr_pourtour\pourtour()
	Next
	
	
	;}
	
	;{ liste des cotés
	
	NewList cote_gauche.POINT()
	NewList cote_droit.POINT()
	
	;{ test pour savoir de quel coté partir
	ChangeCurrentElement(tour_incline(), *adr_haut)
	*elem = PreviousElement(tour_incline())
	If *elem = 0 : *elem = LastElement(tour_incline()) : EndIf
	
	angle = ATan2(*elem\x - *adr_haut\x, *elem\y - *adr_haut\y)
	
	ChangeCurrentElement(tour_incline(), *adr_haut)
	*elem = NextElement(tour_incline())
	If *elem = 0 : *elem = FirstElement(tour_incline()) : EndIf
	
	If angle > ATan2(*elem\x - *adr_haut\x, *elem\y - *adr_haut\y)
		sens = -1
	Else
		sens = 1
	EndIf
	
	;}
	
	;{ parcours pour coté gauche
	; init
	*elem = *adr_haut : *cote = 0
	ChangeCurrentElement(tour_incline(), *adr_haut)
	
	Repeat
		If sens = 1
			*elem_suivant.POINT = NextElement(tour_incline())
			If *elem_suivant = 0 : *elem_suivant = FirstElement(tour_incline()) : EndIf
		Else
			*elem_suivant.POINT = PreviousElement(tour_incline())
			If *elem_suivant = 0 : *elem_suivant = LastElement(tour_incline()) : EndIf
		EndIf
		
		
		cst_y = *elem_suivant\y - *elem\y
		cst_x = *elem_suivant\x - *elem\x
		
		For i = *elem\y + 1 To *elem_suivant\y
			*cote = AddElement(cote_gauche())
			
			*cote\y = i
			*cote\x = ((i - *elem\y) * cst_x) / cst_y + *elem\x
			
			CompilerIf #AFFICHE_POINT
				Plot(*bulle\Centre\x + *cote\x, *bulle\Centre\y + *cote\y, #Blue)
			CompilerEndIf
		Next
		
		*elem = *elem_suivant
	Until *elem = *adr_bas
	
	
	;}
	
	;{ parcours pour coté droit
	; init
	*elem = *adr_haut : *cote = 0
	ChangeCurrentElement(tour_incline(), *adr_haut)
	sens = -sens
	
	Repeat
		If sens = 1
			*elem_suivant.POINT = NextElement(tour_incline())
			If *elem_suivant = 0 : *elem_suivant = FirstElement(tour_incline()) : EndIf
		Else
			*elem_suivant.POINT = PreviousElement(tour_incline())
			If *elem_suivant = 0 : *elem_suivant = LastElement(tour_incline()) : EndIf
		EndIf
		
		cst_y = *elem_suivant\y - *elem\y
		cst_x = *elem_suivant\x - *elem\x
		
		For i = *elem\y + 1 To *elem_suivant\y
			*cote = AddElement(cote_droit())
			
			*cote\y = i
			*cote\x = ((i - *elem\y) * cst_x) / cst_y + *elem\x
			
			CompilerIf #AFFICHE_POINT
				Plot(*bulle\Centre\x + *cote\x, *bulle\Centre\y + *cote\y, #Cyan)
			CompilerEndIf
		Next
		
		*elem = *elem_suivant
	Until *elem = *adr_bas
	
	
	;}
	
	If ListSize(cote_droit()) <> ListSize(cote_droit())
		Debug "D = " + ListSize(cote_droit()) + "   G = " + ListSize(cote_gauche())
	EndIf
	
	;{ correction d'une erreur de sens
	
	FirstElement(cote_droit())
	FirstElement(cote_gauche())
	
	If cote_droit()\x < cote_gauche()\x
		NewList tmp.POINT()
		CopyList(cote_droit(), tmp())
		CopyList(cote_gauche(), cote_droit())
		CopyList(tmp(), cote_gauche())
		
		FreeList(tmp())
	EndIf
	;}
	
	;}
	
	
	
	;{ dessin du texte
	
	;{ Allocation du tableau
	w = bulle_MAX\x - bulle_min\x
	h = bulle_MAX\y - bulle_min\y
	
	vect_offset = rayon + 1
	
	Dim image.l(w+1 + vect_offset + rayon, h+1 + vect_offset + rayon)
	;}
	
	;{ Positionnement du texte dans le tableau
	
	
	;{ Simple pour test affichage
	; 	x = vect_offset : y = vect_offset
	; 	
	; 	ForEach *bulle\BF_Text\Text()
	; 		With *bulle\BF_Text\Text()
	; 			
	; 			If x + \Glyphe\Largeur < w And y + \Glyphe\Hauteur < h
	; 				For x1 = 0 To \Glyphe\Largeur
	; 					For y1 = 0 To \Glyphe\Hauteur
	; 						image(x + x1, y + y1) = RGBA(\RGBA\r, \RGBA\g, \RGBA\b, \Glyphe\Image(x1, y1))
	; 					Next
	; 				Next
	; 			EndIf
	; 			
	; 			x + \Glyphe\Largeur
	; 			If x > w
	; 				x = vect_offset
	; 				y + \Glyphe\Hauteur
	; 			EndIf
	; 			
	; 		EndWith
	; 		
	; 	Next
	;}
	
	x = vect_offset : y = vect_offset
	
	ClearDebugOutput()
	
	Select MiseEnForme
		Case #BF_Text_HorizontalCentered
			
			*txt.IBF_Char = FirstElement(*bulle\BF_Text\Text())
			
			FirstElement(cote_droit())
			FirstElement(cote_gauche())
			
			Repeat
				*cote_g.POINT = @cote_gauche()
				*cote_d.POINT = @cote_droit()
				
				If *txt = 0
					Break
				EndIf
				
				ligne_h.l = *txt\Glyphe\Hauteur
				
				;{ établissement des dimensions de la ligne
				
				Repeat
					ligne_l.l = $7FFFFFFF
					REDO = #False
					
					;{ estimation de la longeur maxi possible de la ligne
					ChangeCurrentElement(cote_droit(), *cote_d)
					ChangeCurrentElement(cote_gauche(), *cote_g)
					
					*cote_d_gauch.point = *cote_d
					*cote_g_droit.point = *cote_g
					
					For i = 1 To ligne_h
						
						If cote_gauche()\x > *cote_g_droit\x
							*cote_g_droit = cote_gauche()
						EndIf
						If cote_droit()\x < *cote_d_gauch\x
							*cote_d_gauch = cote_droit()
						EndIf
						
						If NextElement(cote_droit()) = 0 :  Break : EndIf
						If NextElement(cote_gauche()) = 0 :  Break : EndIf
					Next
					ligne_l = *cote_d_gauch\x - *cote_g_droit\x
					
					*cote_d_fin = @cote_droit()
					*cote_g_fin = @cote_gauche()
					;}
					
					;{ contenu de la ligne
					*txt_fin.IBF_Char = *txt
					ChangeCurrentElement(*bulle\BF_Text\Text(), *txt)
					
					l_courrant.l = 0
					last_space.l = 0
					Repeat
						largeur_mot.l = 0
						
						If *txt_fin\Glyphe\char = " " Or *txt_fin\Glyphe\char = #CR$ Or *txt_fin\Glyphe\char = #LF$
							largeur_mot = *txt_fin\Glyphe\Largeur
							
							last_space = *txt_fin\Glyphe\Largeur
						Else
							*txt_fin_mot.IBF_Char = *txt_fin
							While *txt_fin_mot And *txt_fin_mot\Glyphe\char <> " " And *txt_fin_mot\Glyphe\char <> #CR$ And *txt_fin_mot\Glyphe\char <> #LF$
								largeur_mot + *txt_fin_mot\Glyphe\Largeur
								
								*txt_fin_mot.IBF_Char = NextElement(*bulle\BF_Text\Text())
							Wend
							
							PreviousElement(*bulle\BF_Text\Text())
						EndIf
						
						If l_courrant + largeur_mot >= ligne_l
							l_courrant - last_space
							Break
						EndIf
						
						l_courrant + largeur_mot
						
						If *txt_fin\Glyphe\Hauteur > ligne_h
							ligne_h = *txt_fin\Glyphe\Hauteur
							
							REDO = #True
						EndIf
						
						*txt_fin = NextElement(*bulle\BF_Text\Text())
						If *txt_fin = 0 : Break : EndIf
						
						If *txt_fin\Glyphe\char = #CR$ Or *txt_fin\Glyphe\char = #LF$
							If *txt_fin\Glyphe\char = #CR$
								If NextElement(*bulle\BF_Text\Text()) And *bulle\BF_Text\Text()\Glyphe\char = #LF$
									*txt_fin = @*bulle\BF_Text\Text()
								EndIf
							EndIf
							
							Break
						EndIf
					ForEver
					;}
					
				Until REDO = #False
				;}
				
				If *txt_fin = *txt
					;{ pas la place pour une lettre, on passe à la suite
					
					ChangeCurrentElement(cote_droit(), *cote_d)
					ChangeCurrentElement(cote_gauche(), *cote_g)
					
					*cote_g = NextElement(cote_gauche())
					*cote_d = NextElement(cote_droit())
					
					If *cote_d = 0 Or *cote_g = 0
						Break ; POINT DE sortie
					EndIf
					
					;}
				Else
					;{ les paramètres de la ligne sont corrects
					offset_x = (ligne_l - l_courrant) / 2
					
					ChangeCurrentElement(*bulle\BF_Text\Text(), *txt)
					;ChangeCurrentElement(cote_gauche(), *cote_g)
					
					;{ boucle dessin
					Repeat
						For x1 = 0 To *txt\Glyphe\Largeur
							For y1 = 0 To *txt\Glyphe\Hauteur
								_x = vect_offset + -bulle_min\x + *cote_g_droit\x + offset_x + x1
								_y = vect_offset + -bulle_min\y + *cote_g\y + y1
								
								If _x > 0 And _x =< w + rayon And _y > 0 And _y =< h + rayon
									image(_x, _y) = RGBA(*txt\RGBA\r, *txt\RGBA\g, *txt\RGBA\b, *txt\Glyphe\Image(x1, y1))
								EndIf
							Next
						Next
						
						offset_x + *txt\Glyphe\Largeur
						
						*txt = NextElement(*bulle\BF_Text\Text())
						
						If *txt = 0
							Break 2 ; POINT DE sortie
						EndIf
						
					Until *txt = *txt_fin
					;}
					
					If *cote_d_fin = 0 Or *cote_g_fin = 0
						Break
					EndIf
					
					ChangeCurrentElement(cote_droit(), *cote_d_fin)
					ChangeCurrentElement(cote_gauche(), *cote_g_fin)
					
					;}
				EndIf
				
			ForEver
	EndSelect
	

	;}
	
	
	;{ Affichage
	
	If rayon > 0
		Dim image_gray.a(w+1, h+1)
		
		IBF_GetGrayArray(image(), image_gray())
		
		IBF_GetEdgeGray(image_gray(), rayon)
		
		IBF_CombineArray(image(), image_gray(), @Couleur_contour)
	EndIf
	
	; dessin
	IBF_DrawRotatedArray(image(), *bulle\Centre\x, *bulle\Centre\y, bulle_min\x - vect_offset + 1, bulle_min\y - vect_offset + 1, *bulle\angle_text, *bulle\USE_Antialisating, coef_sigmoide)
	DrawingMode(#PB_2DDrawing_Default)
	
	;}
	
	;}
	
	
	
	;{ libération des ressources
	If *bulle\USE_CattmullRom
		FreeList(*adr_pourtour\pourtour())
		FreeMemory(*adr_pourtour)
	EndIf
	;}
	
EndProcedure



Procedure DrawContourControl(*bulle.BT_Bulle, *souris.POINT, eventtype.l, GD.l, DRAW = #True)
	PAS_FAIT = #True
	
	Static CLIC.l = -1
	
	SetGadgetAttribute(GD, #PB_Canvas_Cursor, #PB_Cursor_Default)
	ForEach *bulle\pourtour()
		If PAS_FAIT
			If eventtype = #PB_EventType_MouseMove
				If CLIC = -1
					dx = *bulle\Centre\x + *bulle\pourtour()\x - *souris\x
					dy = *bulle\Centre\y + *bulle\pourtour()\y - *souris\y
					
					If Sqr(dx * dx + dy * dy) =< 5
						SetGadgetAttribute(GD, #PB_Canvas_Cursor, #PB_Cursor_Arrows)
						PAS_FAIT = #False
					EndIf
				ElseIf ListIndex(*bulle\pourtour()) = CLIC
					*bulle\pourtour()\x = *souris\x - *bulle\Centre\x
					*bulle\pourtour()\y = *souris\y - *bulle\Centre\y
					
					SetGadgetAttribute(GD, #PB_Canvas_Cursor, #PB_Cursor_Arrows)
					PAS_FAIT = #False
					
					If draw = #False
						ProcedureReturn #True
					EndIf
				EndIf
				
			ElseIf eventtype = #PB_EventType_LeftButtonDown
				dx = *bulle\Centre\x + *bulle\pourtour()\x - *souris\x
				dy = *bulle\Centre\y + *bulle\pourtour()\y - *souris\y
				
				If Sqr(dx * dx + dy * dy) =< 5
					CLIC = ListIndex(*bulle\pourtour())
					
					SetGadgetAttribute(GD, #PB_Canvas_Cursor, #PB_Cursor_Arrows)
					PAS_FAIT = #False
				EndIf
			ElseIf eventtype = #PB_EventType_LeftButtonUp
				CLIC = -1
				
			EndIf
			
		EndIf
		
		If DRAW
			DrawingMode(#PB_2DDrawing_Outlined)
			Circle(*bulle\Centre\x + *bulle\pourtour()\x, *bulle\Centre\y + *bulle\pourtour()\y, 5, #Red)
			
			DrawingMode(#PB_2DDrawing_Default)
			Circle(*bulle\Centre\x + *bulle\pourtour()\x, *bulle\Centre\y + *bulle\pourtour()\y, 3, #Red)
		EndIf
	Next
EndProcedure


;}


;{ fenêtre

WD_principale = OpenWindow(#PB_Any, 0, 0, 900, 600, "Bulles de Texte", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
If WD_principale
	GD_canvas = CanvasGadget(#PB_Any, 0, 0, 700, 600, #PB_Canvas_Keyboard)
	
	GD_Editor = EditorGadget(#PB_Any, 700, 0, 200, 100)
	
	TextGadget(#PB_Any, 705, 120, 200, 20, "Taille du Texte")
	GD_Taille = TrackBarGadget(#PB_Any, 705, 140, 190, 20, 5, 30)
	TextGadget(#PB_Any, 705, 160, 200, 20, "Angle du Texte")
	GD_angle = TrackBarGadget(#PB_Any, 705, 180, 190, 20, 0, 350)
	TextGadget(#PB_Any, 705, 200, 200, 20, "Zoom bulle")
	GD_marge = TrackBarGadget(#PB_Any, 705, 220, 190, 20, 10, 150)
	TextGadget(#PB_Any, 705, 240, 240, 20, "Rayon Contour")
	GD_rayon = TrackBarGadget(#PB_Any, 705, 260, 190, 20, 0, 10)
	
	TextGadget(#PB_Any, 705, 300, 200, 20, "Texte RGBA")
	GD_couleurT_R = TrackBarGadget(#PB_Any, 705, 320, 190, 20, 0, 255)
	GD_couleurT_G = TrackBarGadget(#PB_Any, 705, 340, 190, 20, 0, 255)
	GD_couleurT_B = TrackBarGadget(#PB_Any, 705, 360, 190, 20, 0, 255)
	GD_couleurT_A = TrackBarGadget(#PB_Any, 705, 380, 190, 20, 0, 255)
	
	TextGadget(#PB_Any, 705, 400, 200, 20, "Contour RGBA")
	GD_couleurC_R = TrackBarGadget(#PB_Any, 705, 420, 190, 20, 0, 255)
	GD_couleurC_G = TrackBarGadget(#PB_Any, 705, 440, 190, 20, 0, 255)
	GD_couleurC_B = TrackBarGadget(#PB_Any, 705, 460, 190, 20, 0, 255)
	GD_couleurC_A = TrackBarGadget(#PB_Any, 705, 480, 190, 20, 0, 255)
	
	GD_Check_Catmull = CheckBoxGadget(#PB_Any, 705, 500, 200, 20, "Utiliser le lissage")
	GD_Check_AntiAliasing = CheckBoxGadget(#PB_Any, 705, 520, 200, 20, "Anti-aliasser la Police")
	
	GD_LoadFONT = ButtonGadget(#PB_Any, 705, 550, 190, 20, "Changer de Police")
	
	GD_Coef_Sigmoide = TrackBarGadget(#PB_Any, 705, 580, 190, 20, 0, 100)
	
	AddKeyboardShortcut(WD_principale, #PB_Shortcut_Escape, 0)
	
	SetGadgetState(GD_marge, 100)
	SetGadgetState(GD_angle, 330)
	SetGadgetState(GD_rayon, 3)
	
	SetGadgetState(GD_couleurT_R, 80)
	SetGadgetState(GD_couleurT_G, 255)
	SetGadgetState(GD_couleurT_B, 0)
	SetGadgetState(GD_couleurT_A, 255)
	
	SetGadgetState(GD_couleurC_R, 170)
	SetGadgetState(GD_couleurC_G, 0)
	SetGadgetState(GD_couleurC_B, 200)
	SetGadgetState(GD_couleurC_A, 255)
	
	SetGadgetState(GD_Check_Catmull, #True)
	SetGadgetState(GD_Check_AntiAliasing, #True)
	
	SetFocus_(GadgetID(GD_angle))
EndIf

font_display = LoadFont(#PB_Any, "Arial Black", 12)

;}

;{ création de la bulle de test

*bulle.bt_Bulle = BT_InitBulleText(#True)


If ReadFile(0, "Loresdfm Ipsum.txt")
	*bulle\text = ""
	
	Repeat
		*bulle\text = *bulle\text + ReadString(0)
		If Eof(0) = 0
			*bulle\text + #CR$
		EndIf
	Until Eof(0)
Else
	*bulle\text = "Interloqué, David ne sait pas quoi répondre. "+
	              "En effet, il connaît un certain Prélude : lui."+
	              "C'était le pseudo qu'il utilisait dans sa jeunesse "+
	              "d'informaticien. Tout ses amis de l'époque le connaissaient "+
	              "sous ce nom. Il l'avait utilisé une fois ou deux pour signer "+
	              "les logiciels qu'il avait piraté. Mais jamais un logiciel "+
	              "important et encore moins un logiciel top secret. "+
	              "C'était également le surnom qu'il utilisait à la faculté." + #CR$ + #CR$ + 
	              "Les deux gardes du corps personnels de David le prirent "+
	              "par le bras et suivirent le général. Les militaires s'étaient "+
	              "mis au « garde à vous » sur les côtés du couloir. "+
	              "Celui-ci menait à un ascenseur. Le général inséra à "+
	              "nouveau son badge et la porte s'ouvrit. Il y montèrent tous les quatre. "+
	              "Il n'y avait pas de niveau d'indiqué."
EndIf

*bulle\Centre\x = 400
*bulle\Centre\y = 300

*bulle\color = #White

AddElement(*bulle\pourtour()) : *bulle\pourtour()\x = 0 : *bulle\pourtour()\y = -200
AddElement(*bulle\pourtour()) : *bulle\pourtour()\x = -100 : *bulle\pourtour()\y = -150
AddElement(*bulle\pourtour()) : *bulle\pourtour()\x = -80 : *bulle\pourtour()\y = 110
AddElement(*bulle\pourtour()) : *bulle\pourtour()\x = 50 : *bulle\pourtour()\y = 120
AddElement(*bulle\pourtour()) : *bulle\pourtour()\x = 150 : *bulle\pourtour()\y = -125

;{ centrage vite fait
c.POINT
ForEach *bulle\pourtour()
	c\x + *bulle\pourtour()\x
	c\y + *bulle\pourtour()\y
Next

c\x / ListSize(*bulle\pourtour())
c\y / ListSize(*bulle\pourtour())

ForEach *bulle\pourtour()
	*bulle\pourtour()\x - c\x
	*bulle\pourtour()\y - c\y
Next
;}

SetGadgetText(GD_Editor, *bulle\text)
PostEvent(#PB_Event_Gadget, WD_principale, GD_Editor, #PB_EventType_Change)

font_size = 15
font_name.s = "Arial"
font_style = 0

reload_font = #True

SetGadgetState(GD_Taille, font_size)

;}

Define mouse.POINT


Repeat
	event = WaitWindowEvent()
	
	;{ event
	
	If event = #PB_Event_Menu
		If EventMenu() = 0
			event = #PB_Event_CloseWindow
		EndIf
		
		
	ElseIf event = #PB_Event_Gadget
		If EventGadget() = GD_Editor
			If EventType() = #PB_EventType_Change
				
				*bulle\text = GetGadgetText(GD_Editor)
				
				redraw = #True
				reload_font = #True
			EndIf
			
		ElseIf EventGadget() = GD_Taille
			redraw = #True
			
			font_size = GetGadgetState(GD_Taille)
			reload_font = #True
			
		ElseIf EventGadget() = GD_LoadFONT
			redraw = #True
			
			If FontRequester(font_name, font_size, 0, font_style)
				font_name = SelectedFontName()
				font_style = SelectedFontStyle()
			EndIf
			
			reload_font = #True
			
		ElseIf EventGadget() = GD_Coef_Sigmoide
			redraw = #True
			
		ElseIf EventGadget() = GD_angle
			redraw = #True
			
		ElseIf EventGadget() = GD_marge
			redraw = #True
			
		ElseIf EventGadget() = GD_rayon
			redraw = #True
			
		ElseIf EventGadget() = GD_canvas
			
			canvas_eventtype = EventType()
			
			mouse\x = GetGadgetAttribute(GD_canvas, #PB_Canvas_MouseX)
			mouse\y = GetGadgetAttribute(GD_canvas, #PB_Canvas_MouseY)
			
			If DrawContourControl(*bulle, @mouse, canvas_eventtype, GD_canvas, #False)
				redraw = #True
			EndIf
			
		ElseIf EventGadget() = GD_couleurT_R Or EventGadget() = GD_couleurT_G Or EventGadget() = GD_couleurT_B
			redraw = #True
			
			*bulle\color = RGBA(GetGadgetState(GD_couleurT_R), GetGadgetState(GD_couleurT_G), GetGadgetState(GD_couleurT_B), GetGadgetState(GD_couleurT_A))
			
			ForEach *bulle\BF_Text\Text()
				*bulle\BF_Text\Text()\couleur = *bulle\color
			Next
			
		ElseIf EventGadget() = GD_couleurC_R Or EventGadget() = GD_couleurC_G Or EventGadget() = GD_couleurC_B Or EventGadget() = GD_couleurC_A
			redraw = #True
			
		ElseIf EventGadget() = GD_Check_Catmull
			redraw = #True
			
			*bulle\USE_CattmullRom = GetGadgetState(GD_Check_Catmull)
			
		ElseIf EventGadget() = GD_Check_AntiAliasing
			redraw = #True
			
			*bulle\USE_Antialisating = GetGadgetState(GD_Check_AntiAliasing)
		EndIf
	EndIf
	;}
	
	;{ reload font
	
	If reload_font
		reload_font = #False
		
		If IsFont(font_test) : FreeFont(font_test) : EndIf
		font_test = LoadFont(#PB_Any, font_name, font_size, font_style)
		
		*font.IBF_Font = BF_LoadFont(font_name, font_size, font_style)
		
		FreeList(*bulle\BF_Text\Text())
		FreeMemory(*bulle\BF_Text)
		
		*bulle\color = RGBA(GetGadgetState(GD_couleurT_R), GetGadgetState(GD_couleurT_G), GetGadgetState(GD_couleurT_B), GetGadgetState(GD_couleurT_A))
		*bulle\BF_Text = BF_NewText(*bulle\text, *font, *bulle\color)
	EndIf
	
	;}
	
	;{ redraw
	
	If redraw
		*bulle\angle_text = Radian(GetGadgetState(GD_angle))
		
		StartDrawing(CanvasOutput(GD_canvas))
		DrawingFont(FontID(font_test))
		
		Box(0, 0, 700, 600, RGB(200, 200, 200))
		
		*bulle\Centre\x = 150
		DrawContourControl(*bulle, @mouse, canvas_eventtype, GD_canvas)
		
		
		
		time = ElapsedMilliseconds()
		*bulle\Centre\x = 530
		BT_DrawBulleText(*bulle, GetGadgetState(GD_marge) / 100)
		time = ElapsedMilliseconds() - time
		
		
		time2 = ElapsedMilliseconds()
		*bulle\Centre\x = 150
		BT_DrawBulleText_(*bulle, GetGadgetState(GD_marge) / 100, GetGadgetState(GD_rayon), RGBA(GetGadgetState(GD_couleurC_R), GetGadgetState(GD_couleurC_G), GetGadgetState(GD_couleurC_B), GetGadgetState(GD_couleurC_A)), GetGadgetState(GD_Coef_Sigmoide) / 100)
		time2 = ElapsedMilliseconds() - time2
		
		DrawingFont(FontID(font_display))
		DrawingMode(#PB_2DDrawing_Transparent)
		
		DrawText(480, 10, "DrawRotatedText()")
		DrawText(530, 30, Str(time) + " ms")
		
		DrawText(100, 10, "Procedure Perso")
		DrawText(150, 30, Str(time2) + " ms")
		
		DrawText(10, OutputHeight() - 30, "[Haut / Bas] pour varier la taille de la police")
		
		StopDrawing()
		redraw = #False
	EndIf
	
	;}
	
Until event = #PB_Event_CloseWindow

End
Dernière modification par graph100 le lun. 24/mars/2014 21:09, modifié 1 fois.
_________________________________________________
Mon site : CeriseCode (Attention Chantier perpétuel ;))
Avatar de l’utilisateur
djes
Messages : 4252
Inscription : ven. 11/févr./2005 17:34
Localisation : Arras, France

Re: Opération sur les Fonts

Message par djes »

Wow, c'est super impressionnant ! 8O
Bravo ! :D
Avatar de l’utilisateur
Fig
Messages : 1176
Inscription : jeu. 14/oct./2004 19:48

Re: Opération sur les Fonts

Message par Fig »

Très très bon travail !
8O
Il y a deux méthodes pour écrire des programmes sans erreurs. Mais il n’y a que la troisième qui marche.
Version de PB : 6.00LTS - 64 bits
Avatar de l’utilisateur
Droopy
Messages : 1151
Inscription : lun. 19/juil./2004 22:31

Re: Opération sur les Fonts

Message par Droopy »

Un grand bravo pour ce code, même si j'y comprends rien.
Backup
Messages : 14526
Inscription : lun. 26/avr./2004 0:40

Re: Opération sur les Fonts

Message par Backup »

graph100 a écrit :Quelqu'un a une idée pour atténuer le lissage du à l'antialiasing du texte ?
quelques Algo ici :)
http://www.purebasic.fr/french/viewtopi ... =1&t=14272
nico
Messages : 3702
Inscription : ven. 13/févr./2004 0:57

Re: Opération sur les Fonts

Message par nico »

Wow, c'est super impressionnant ! 8O
Pareil, les mêmes mots me sont venus à l'esprit quand j'ai vu le résultat de ce code.

Cela faisait longtemps que je n'avais été autant impressionné par un code, spectaculaire, bravô.
Avatar de l’utilisateur
graph100
Messages : 1318
Inscription : sam. 21/mai/2005 17:50

Re: Opération sur les Fonts

Message par graph100 »

Merci les copains :D
Droopy a écrit :Un grand bravo pour ce code, même si j'y comprends rien.
Bah la partie écriture du texte, moi non plus je suis pas sur de pouvoir me relire :lol:
Mais ça fonctionne plutôt pas mal.

Dobro je regarde ça :wink:
édit : c'est pas l'antialiasing du texte qui gène, mais plutôt que lorsque le texte est pivoté, chaque pixel est antialiasé, et ça donne un effet de flou qui peut être gênant pour les petits détails
Dernière modification par graph100 le lun. 24/mars/2014 20:12, modifié 1 fois.
_________________________________________________
Mon site : CeriseCode (Attention Chantier perpétuel ;))
Avatar de l’utilisateur
djes
Messages : 4252
Inscription : ven. 11/févr./2005 17:34
Localisation : Arras, France

Re: Opération sur les Fonts

Message par djes »

graph100 a écrit : édit : c'est pas l'aliasing du text qui gène, mais plutot que lorsque le texte est pivoté, chaque pixel est antialiasé, et ça donne un effet de flou qui peu être gènant pour les petits détails
Tu peux peut-être limiter l'antialiasing en ne l'appliquant qu'au-delà d'un certain seuil. De mémoire, il faut se baser sur la composante bleue pour avoir les meilleurs résultats, mais je ne suis pas sûr.
Avatar de l’utilisateur
GallyHC
Messages : 1703
Inscription : lun. 17/déc./2007 12:44

Re: Opération sur les Fonts

Message par GallyHC »

impressionnant :) on sent le travail et le travail de recherche...

GallyHC
Configuration : Tower: Windows 10 (Processeur: i7 "x64") (Mémoire: 16Go) (GeForce GTX 760 - 2Go) - PureBasic 5.72 (x86 et x64)
Avatar de l’utilisateur
graph100
Messages : 1318
Inscription : sam. 21/mai/2005 17:50

Re: Opération sur les Fonts

Message par graph100 »

djes a écrit :Tu peux peut-être limiter l'antialiasing en ne l'appliquant qu'au-delà d'un certain seuil. De mémoire, il faut se baser sur la composante bleue pour avoir les meilleurs résultats, mais je ne suis pas sûr.
Pour l'appliquer sur une seule composante il faudrait calculer les coefs pour le rouge / vert / et bleu séparément !
Ça peut se faire, mais c'est plus long je pense :roll:

J'ai testé une méthode différente, qui consiste à modifier les coefs de la matrice d'antialiasing de manière à faire tendre les valeurs en dessous de 0.5 vers 0, et celle au-dessus vers 1, avec la formule suivante :

Code : Tout sélectionner

Macro Sigmoide(_x_)
	_x_ *(1-Coef_Sigmoide) + (Coef_Sigmoide) * ((1+TanH((-0.5+_x_) * Coef_Sigmoide * 10))/2)
EndMacro
Avec Coef_Sigmoide une valeur entre 0 et 1 qui définie si la formule utilisée est plus linéaire que sigmoïde ou non.
(0 --> linéaire, 1 --> sigmoïde)

J'ai mis à jour le code du post précédant avec une TrackBar supplémentaire qui contrôle Coef_Sigmoide.
Vos impressions ?

On peut aussi constater le cout processeur de l'ajout de la formule en comparant le temps de dessin pour Coef_Sigmoide = 0 et Coef_Sigmoide > 0.
Ça double le temps de dessin sur mon ordi (30ms -> 60ms)

Sur des images plus grande il faudrait ajouter une table pour aller plus vite !
_________________________________________________
Mon site : CeriseCode (Attention Chantier perpétuel ;))
Avatar de l’utilisateur
djes
Messages : 4252
Inscription : ven. 11/févr./2005 17:34
Localisation : Arras, France

Re: Opération sur les Fonts

Message par djes »

Ca fonctionne bien :) C'est la base d'un logiciel de PAO, avec gestion dynamique du texte ; il a fallu des années à certains éditeurs pour arriver à ce résultat...
Répondre