Bonjour,

Un de mes programmes utilise des données BMP (image RGB avec 1 bit, 8 bit ou 24 bits par pixel)

Le code principal lit le fichier directement et travaille sur un tableau, ça fonctionne bien.

Dans ce programme, j'ai intégré un outil qui est en quelque sorte un clone de MS Paint.
Il permet à l'utilisateur d'éditer l'image, avec des fonctions spécifiques utile pour le reste de mon programme.

En utilisant les classes Imaging Graphics et Bitmap, j'arrive assez facilement à afficher l'image, la déplacer à la souris, zoomer, faire des fonctions de traitement global, dessiner avec la souris.

Mais je rencontre les inconvénients suivants :
- je suis obligé de travailler avec des bitmap 32 bits ; donc à la lecture je dois convertir mon bitmap en 32 bits, et à la sauvegarde je dois le convertir à nouveau
- c'est lent, particulièrement le dessin à la souris

Je dois certainement refaire mon code pour travailler sur des tableaux...
...mais à chaque affichage il faut inévitablement passer par un bitmap et un graphics

La conversion tableau -> objet bitmap "affichable" doit être rapide
Ou alors il ne faut pas convertir, pour avoir un tableau dont le contenu correspond à un objet bitmap et juste "passer son adresse" ; mais ce serait un très gros gaspillage de mémoire lorsque je travaille sur un très grand bitmap 1 bit...

J'aimerais aussi trouver l'équivalent de la synchronisation d'Open.GL (GlControlXXX.Context.VSync = True) ; en effet il est stupide de commander l'affichage plus rapidement que la période de rafraîchissement de l'écran, on surcharge inutilement l'ordinateur.

Éventuellement.... utiliser OpenGL en 2D au lieu des fonctions la GDI+ ???
Si mon code transformant le tableau en texture affichable par OpenGL est rapide ça serait une solution

(d'autres parties de mon programme utilisent déjà OpenGL en 3D et en 2D vectoriel et c'est très rapide)

Je ne vois pas trop comment m'y prendre

Voici le code (il faut une fenêtre redimenssionnable Form1 avec un picture box "ImgBMP" dont le Dock est Fill) :

Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
Public Class Form1
    Private Enum ActionSouris 'Pour savoir ce que l'utilisateur est en train de faire avec la souris lors d'un mouse move
        Aucune = 0
        BmpMove = 5
        BmpEdit = 6
    End Enum
 
    Private Autorisation As Boolean 'Desactive la gestion des évenements si un autre est en cours
    Private AutorisationPaint As Boolean 'Permet d'éviter les appels multiples de Peindre()
    Private AutorisationClic As Boolean 'Autorise le clic même si Autorisation = False
    Private ActionM As ActionSouris
    Private EtatBouton As Integer
    Private XPrec As Integer, YPrec As Integer 'Coordonnées 2D du curseur de la souris
 
    Private BMP_G As Graphics
    Private BMP_B As Bitmap 'Corresponds à l'image en mémoire (données)
    Private BMP_B2 As Bitmap 'Corresponds à la picture box dans la fenêtre pour l'affichage à l'écran
    Private BMP_W As Integer
    Private BMP_H As Integer
    Private BMP_X0 As Integer
    Private BMP_Y0 As Integer
    Private BMP_Zoom As Double
    Private BMP_invZoom As Double
    Private Const BMP_ZoomMin As Double = 0.25
    Private Const BMP_ZoomMax As Double = 40
    Private BMP_Coul1 As Color
    Private BMP_Coul2 As Color
    Private BMP_Coul3 As Color
    Private BMP_EstModifie As Boolean
    Private BMP_CouleurFond As Color
 
    Private BMP_Fichier As String
 
    Public Sub BMP_Montrer(Fichier As String) 'C'est cette fonction qui est appellée pour lancer l'utilitaire de dessin
        Dim BMP_B3 As Bitmap
        Dim BMP_G3 As Graphics
 
        Autorisation = False
        AutorisationPaint = False
        AutorisationClic = False
        ActionM = ActionSouris.Aucune
 
        BMP_EstModifie = False
 
        BMP_Fichier = Fichier
 
        BMP_B3 = New Bitmap(BMP_Fichier) 'Vérifications faites sur le fichier par le code appellant
        BMP_W = BMP_B3.Width
        BMP_H = BMP_B3.Height
        BMP_B = New Bitmap(BMP_W, BMP_H)
        BMP_G3 = Graphics.FromImage(BMP_B)
        BMP_G3.DrawImage(BMP_B3, 0, 0, BMP_W, BMP_H) 'Nécessaire car l'encodage du fichier BMP ne correspond pas forcément à celui de l'écran
 
        BMP_X0 = 0
        BMP_Y0 = 0
        BMP_Zoom = 1
        BMP_invZoom = 1 / BMP_Zoom
 
        BMP_Coul1 = Color.White
        BMP_Coul2 = Color.Black
        BMP_Coul3 = Color.Red
 
        BMP_CouleurFond = Color.Gray
 
        BMP_B2 = New Bitmap(ImgBMP.Width, ImgBMP.Height)
        BMP_G = Graphics.FromImage(BMP_B2)
        BMP_G.SmoothingMode = Drawing2D.SmoothingMode.None 'Important permet de zoomer proprement en voyant les pixels bien carrés
        BMP_G.InterpolationMode = Drawing2D.InterpolationMode.NearestNeighbor 'Important permet de zoomer proprement en voyant les pixels bien carrés
 
        Call BMP_AfficherTitre(0, 0)
        Autorisation = True
        AutorisationPaint = True
        Me.ShowDialog()
    End Sub
    Private Sub Form1_Shown(sender As Object, e As EventArgs) Handles Me.Shown
        Call BMP_Peindre()
    End Sub
 
    '*** EDITION BMP *** Dessin ***************************************************************************************************
 
    Private Sub ImgBMP_Paint(sender As Object, e As PaintEventArgs) Handles ImgBMP.Paint
        If AutorisationPaint Then
            AutorisationPaint = False
            Call BMP_Peindre()
        End If
    End Sub
    Public Sub BMP_Peindre() 'Fonction d'actualisation de l'affichage
        ImgBMP.Image = BMP_B2
        BMP_G.Clear(BMP_CouleurFond)
        'On dessine tout, il faudrait dessinner que le rectangle utile de l'image d'origine ???
        BMP_G.DrawImage(BMP_B, BMP_X0, BMP_Y0, CInt(BMP_W * BMP_Zoom), CInt(BMP_H * BMP_Zoom))
        'Important pour éviter des appels multiples :
        My.Application.DoEvents()
        AutorisationPaint = True
    End Sub
    Private Sub ImgBMP_SizeChanged(sender As Object, e As EventArgs) Handles ImgBMP.SizeChanged
        If Autorisation AndAlso AutorisationPaint Then
            Autorisation = False
            AutorisationPaint = False
            BMP_B2 = New Bitmap(ImgBMP.Width, ImgBMP.Height) 'Nécessaire car le BMP_B peut être sur 8 bits ou autre... différent de l'écran utilisé
            BMP_G = Graphics.FromImage(BMP_B2)
            BMP_G.SmoothingMode = Drawing2D.SmoothingMode.None
            BMP_G.InterpolationMode = Drawing2D.InterpolationMode.NearestNeighbor
            Call BMP_Peindre()
            Autorisation = True
        End If
    End Sub
 
    Private Sub BMP_AfficherTitre(X As Integer, Y As Integer)
        Dim s As New System.Text.StringBuilder
        If BMP_EstModifie Then
            s.Append("[X] - <")
        Else
            s.Append("[ ] - <")
        End If
        s.Append(BMP_Fichier)
        s.Append(">  X = ")
        s.Append(X.ToString("0"))
        s.Append(">  Y = ")
        s.Append(Y.ToString("0"))
        Me.Text = s.ToString
    End Sub
 
    '*** EDITION BMP *** Gestion souris *******************************************************************************************
 
    Private Sub Form1_MouseWheel(sender As Object, e As System.Windows.Forms.MouseEventArgs) Handles Me.MouseWheel 'Zoom molette
        Dim tmp As Double, valeur As Double
        Dim p As Point
        If Autorisation OrElse AutorisationClic Then
            Autorisation = False
            p = ImgBMP.PointToClient(MousePosition)
            valeur = e.Delta
            If valeur > 0 Then
                tmp = BMP_Zoom * (1 + 0.002 * valeur)
            Else
                tmp = BMP_Zoom / (1 + 0.002 * -valeur)
            End If
            If tmp >= BMP_ZoomMin AndAlso tmp <= BMP_ZoomMax Then
                'Point sous la souris invariant lors du zoom :
                BMP_X0 = CInt(p.X - (p.X - BMP_X0) * tmp * BMP_invZoom)
                BMP_Y0 = CInt(p.Y - (p.Y - BMP_Y0) * tmp * BMP_invZoom)
                BMP_Zoom = tmp
                BMP_invZoom = 1 / BMP_Zoom
                Call BMP_Peindre()
            End If
            Autorisation = True
        End If
    End Sub
    Private Sub ImgBMP_MouseDown(sender As Object, e As MouseEventArgs) Handles ImgBMP.MouseDown
        Dim X As Integer
        Dim Y As Integer
        Dim ke As Keys
 
        If Autorisation OrElse AutorisationClic Then
            Autorisation = False
            ke = Control.ModifierKeys
            If ke = Keys.None Then 'Déplacement :
                ActionM = ActionSouris.BmpMove
                EtatBouton = 0
                If e.Button = Windows.Forms.MouseButtons.Left Then
                    EtatBouton = 1
                    XPrec = e.X
                    YPrec = e.Y
                End If
            Else 'Dessin (retouche pixel par pixel) :
                X = CInt((e.X - BMP_X0) * BMP_invZoom)
                Y = CInt((e.Y - BMP_Y0) * BMP_invZoom)
                If X >= 0 AndAlso Y >= 0 AndAlso X < BMP_W AndAlso Y < BMP_H Then
                    ActionM = ActionSouris.BmpEdit
                    AutorisationPaint = False
                    If e.Button = MouseButtons.Left Then 'dessin :
                        Select Case Control.ModifierKeys
                            Case Keys.Shift : BMP_B.SetPixel(X, Y, BMP_Coul1) : BMP_EstModifie = True
                            Case Keys.Alt : BMP_B.SetPixel(X, Y, BMP_Coul2) : BMP_EstModifie = True
                            Case Keys.Control : BMP_B.SetPixel(X, Y, BMP_Coul3) : BMP_EstModifie = True
                        End Select
                    ElseIf e.Button = MouseButtons.Right Then 'picking color :
                        Select Case Control.ModifierKeys
                            Case Keys.Shift : BMP_Coul1 = BMP_B.GetPixel(X, Y)
                            Case Keys.Alt : BMP_Coul2 = BMP_B.GetPixel(X, Y)
                            Case Keys.Control : BMP_Coul3 = BMP_B.GetPixel(X, Y)
                        End Select
                    End If
                    Call BMP_Peindre()
                End If
            End If
            Autorisation = True
        End If
    End Sub
    Private Sub ImgBMP_MouseUp(sender As Object, e As MouseEventArgs) Handles ImgBMP.MouseUp
        ActionM = ActionSouris.Aucune
        EtatBouton = 0
    End Sub
    Private Sub ImgBMP_MouseMove(sender As Object, e As MouseEventArgs) Handles ImgBMP.MouseMove
        Dim DX As Integer, DY As Integer
        Dim X As Integer
        Dim Y As Integer
        Dim ke As Keys
 
        If Autorisation Then
            If ActionM = ActionSouris.BmpMove Then
                Autorisation = False
                AutorisationPaint = False
                AutorisationClic = True
                DX = e.X - XPrec
                DY = e.Y - YPrec
                XPrec = e.X
                YPrec = e.Y
                BMP_X0 += DX
                BMP_Y0 += DY
                Call BMP_AfficherTitre(CInt((e.X - BMP_X0) * BMP_invZoom), CInt((e.Y - BMP_Y0) * BMP_invZoom))
                Call BMP_Peindre()
                AutorisationClic = False
                Autorisation = True
            ElseIf ActionM = ActionSouris.BmpEdit Then
                Autorisation = False
                AutorisationPaint = False
                AutorisationClic = True
                X = CInt((e.X - BMP_X0) * BMP_invZoom)
                Y = CInt((e.Y - BMP_Y0) * BMP_invZoom)
                Call BMP_AfficherTitre(X, Y)
                If X >= 0 AndAlso Y >= 0 AndAlso X < BMP_W AndAlso Y < BMP_H Then
                    ke = Control.ModifierKeys
                    If e.Button = MouseButtons.Left Then
                        Select Case Control.ModifierKeys
                            Case Keys.Shift : BMP_B.SetPixel(X, Y, BMP_Coul1) : BMP_EstModifie = True
                            Case Keys.Alt : BMP_B.SetPixel(X, Y, BMP_Coul2) : BMP_EstModifie = True
                            Case Keys.Control : BMP_B.SetPixel(X, Y, BMP_Coul3) : BMP_EstModifie = True
                        End Select
                    ElseIf e.Button = MouseButtons.Right Then
                        Select Case Control.ModifierKeys
                            Case Keys.Shift : BMP_Coul1 = BMP_B.GetPixel(X, Y)
                            Case Keys.Alt : BMP_Coul2 = BMP_B.GetPixel(X, Y)
                            Case Keys.Control : BMP_Coul3 = BMP_B.GetPixel(X, Y)
                        End Select
                    End If
                    Call BMP_Peindre()
                End If
                AutorisationClic = False
                Autorisation = True
            Else
                Call BMP_AfficherTitre(CInt((e.X - BMP_X0) * BMP_invZoom), CInt((e.Y - BMP_Y0) * BMP_invZoom))
            End If
        End If
 
    End Sub
 
    '*** EDITION BMP *** Gestion clavier ******************************************************************************************
 
    Protected Overrides Function ProcessCmdKey(ByRef msg As Message, ByVal keyData As Keys) As Boolean
        'If ModeFenetreEditionBMP Then
        If ProcessCmdKey_BMP(keyData) Then
            Return True
        Else
            Return MyBase.ProcessCmdKey(msg, keyData)
        End If
        'Else
        'Return MyBase.ProcessCmdKey(msg, keyData)
        'End If
    End Function
 
    Private Function ProcessCmdKey_BMP(ByVal keyData As Keys) As Boolean
        Dim tmp As Double
        Const DZ As Double = 0.2
        Dim RES As Integer '0 : redessiner / 1 : pas besoin de redessiner / 2 : redonner la main / 3 : calculer le zoom et redessiner
        Dim p As Point
        Dim X As Integer, Y As Integer
 
        If Autorisation Then
            Autorisation = False
            AutorisationPaint = False
            p = ImgBMP.PointToClient(MousePosition)
 
            RES = 2
 
            Select Case keyData
                'Commandes standards
                Case Keys.Escape
                    Me.Hide()
                    RES = 1
                Case Keys.F1
                    'Call Aide()
                    RES = 1
                Case Keys.S
                    Call BMP_Sauver()
                    RES = 1
 
                'Modifications globales sur l'image :
                Case Keys.F5
                    BMP_B.RotateFlip(RotateFlipType.RotateNoneFlipX)
                    BMP_EstModifie = True
                    RES = 0
                Case Keys.F6
                    BMP_B.RotateFlip(RotateFlipType.RotateNoneFlipY)
                    BMP_EstModifie = True
                    RES = 0
                Case Keys.F7
                    BMP_B.RotateFlip(RotateFlipType.Rotate90FlipNone)
                    BMP_W = BMP_B.Width
                    BMP_H = BMP_B.Height
                    BMP_EstModifie = True
                    RES = 0
                Case Keys.F8
                    BMP_B.RotateFlip(RotateFlipType.Rotate270FlipNone)
                    BMP_W = BMP_B.Width
                    BMP_H = BMP_B.Height
                    BMP_EstModifie = True
                    RES = 0
            End Select
 
            X = p.X
            Y = p.Y
            If RES = 2 AndAlso X >= 0 AndAlso Y >= 0 AndAlso X < ImgBMP.Width AndAlso Y < ImgBMP.Height Then 'Commandes déplacement
                Select Case keyData
                    Case Keys.NumPad0, Keys.D0 'Reset view
                        BMP_X0 = 0
                        BMP_Y0 = 0
                        BMP_Zoom = 1
                        BMP_invZoom = 1 / BMP_Zoom
                        RES = 0
                    Case Keys.Down 'Translation au clavier
                        BMP_Y0 += ImgBMP.Height \ 5
                        RES = 0
                    Case Keys.Up 'Translation au clavier
                        BMP_Y0 -= ImgBMP.Height \ 5
                        RES = 0
                    Case Keys.Left 'Translation au clavier
                        BMP_X0 -= ImgBMP.Width \ 5
                        RES = 0
                    Case Keys.Right 'Translation au clavier
                        BMP_X0 += ImgBMP.Width \ 5
                        RES = 0
                    Case Keys.PageUp 'Zoom au clavier
                        tmp = BMP_Zoom * (1 + DZ)
                        If tmp >= BMP_ZoomMin AndAlso tmp <= BMP_ZoomMax Then
                            RES = 3
                        Else
                            RES = 1
                        End If
                    Case Keys.PageDown 'Zoom au clavier
                        tmp = BMP_Zoom / (1 + DZ)
                        If tmp >= BMP_ZoomMin AndAlso tmp <= BMP_ZoomMax Then
                            RES = 3
                        Else
                            RES = 1
                        End If
                End Select
            End If
 
            X = CInt((X - BMP_X0) * BMP_invZoom)
            Y = CInt((Y - BMP_Y0) * BMP_invZoom)
 
            Select Case RES
                Case 3
                    'Calcul zoom avec point sous la souris invariant :
                    BMP_X0 = CInt(p.X - (p.X - BMP_X0) * tmp * BMP_invZoom)
                    BMP_Y0 = CInt(p.Y - (p.Y - BMP_Y0) * tmp * BMP_invZoom)
                    BMP_Zoom = tmp
                    BMP_invZoom = 1 / BMP_Zoom
                    Call BMP_AfficherTitre(X, Y)
                    Call BMP_Peindre()
                    Autorisation = True
                    Return True
                Case 0
                    Call BMP_AfficherTitre(X, Y)
                    Call BMP_Peindre()
                    Autorisation = True
                    Return True
                Case 1
                    Autorisation = True
                    AutorisationPaint = True
                    Return True
                Case Else
                    Autorisation = True
                    AutorisationPaint = True
                    Return False
            End Select
        Else
            Return False
        End If
    End Function
 
    Private Sub BMP_Sauver()
        Dim F As SaveFileDialog
        Dim Fichier As String
        Dim encp As Imaging.EncoderParameters
        Dim codecs As Imaging.ImageCodecInfo()
        Dim codec As Imaging.ImageCodecInfo
        Dim b As Bitmap
        Dim g As Graphics
 
        F = New SaveFileDialog
        F.Filter = "Fichiers BMP (*.bmp)|*.bmp|Tous les fichiers (*.*)|*.*"
        F.FileName = BMP_Fichier & "_EDIT.BMP"
 
        If F.ShowDialog = Windows.Forms.DialogResult.OK Then
            Fichier = F.FileName
            If Fichier <> vbNullString Then
                'A FAIRE : demander si on écrase un fichier existant + gestion d'erreur (fichier lecture seule, ect...)
                codecs = Imaging.ImageCodecInfo.GetImageDecoders()
                For Each codec In codecs
                    If codec.FormatID = Imaging.ImageFormat.Bmp.Guid Then
                        encp = New Imaging.EncoderParameters(1)
                        encp.Param(0) = New Imaging.EncoderParameter(Imaging.Encoder.Compression, CLng(Imaging.EncoderValue.CompressionNone))
                        'BMP_B.Save(Fichier, codec, encp) 'Cette fonction sauve au format "écran" donc 32 bits ARGB
                        b = New Bitmap(BMP_W, BMP_H, Imaging.PixelFormat.Format24bppRgb)
                        g = Graphics.FromImage(b)
                        g.DrawImage(BMP_B, New Rectangle(0, 0, BMP_W, BMP_H))
                        b.Save(Fichier, codec, encp)
                        BMP_EstModifie = False
                        Exit For
                    End If
                Next codec
                'A FAIRE : message d'erreur si on ne trouve pas le codec ?
            End If
        End If
    End Sub
 
End Class
A bientôt