-
Notifications
You must be signed in to change notification settings - Fork 3
/
StdPictureEx.cls
2756 lines (2524 loc) · 153 KB
/
StdPictureEx.cls
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
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
VERSION 1.0 CLASS
BEGIN
MultiUse = -1 'True
Persistable = 0 'NotPersistable
DataBindingBehavior = 0 'vbNone
DataSourceBehavior = 0 'vbNone
MTSTransactionMode = 0 'NotAnMTSObject
END
Attribute VB_Name = "StdPictureEx"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = True
Attribute VB_PredeclaredId = True
Attribute VB_Exposed = False
'========================================================================================================
' Class purpose and usage
'========================================================================================================
' The class is defined as predeclared. This means you do not create an instance of it. An instance is
' created automatically when your project is run, similar to a .bas module. You do not unload it or set
' it to Nothing. You can access its properties/methods directly by its name, i.e., StdPictureEx.LoadPicture
' Requirements: This class requires GDI+ and XP operating system or higher. If the class fails to load
' GDI+ or runs on a system lower than XP, then all functions may fail over to VB's standard picture
' functions as if this class does not even exist. Unicode support may still be provided, but no
' additional enhancements will be available. If you wish to code this for lower operating systems,
' you will need to ensure CreateIconFromResourceEx API usage does not fail. 32bpp icons were introduced
' with XP and will not be supported on lower operating systems. The GDI+ requirement remains.
' Enhancements: These formats are supported: BMP,ICO,CUR,WMF,EMF,JPG,PNG,TIF,GIF.
' Additionally, unicode support is provided for path and file names. Proper 32bpp icon rendering is
' supported. PNG-encoded icons are supported. Caching of supported image formats can be performed and
' saved to file. Navigation of TIF pages and GIF frames are supported. This means you can animate GIFs.
' Depending on system settings, bitmap formats that include color management are supported. In any case,
' bitmaps with both true and premultiplied alpha channels are supported. The LoadPicture and SavePicture
' methods also will accept byte arrays and existing stdPicture objects, along with file names.
' When to refresh picture's container object? When calling the SubImage() method to change the frame of
' an animated GIF or page of a multipage TIF, the container may need to be refreshed. If the return
' result from SubImage() is assigned to a VB image control, generally refresh otherwise not needed.
' When in doubt, you can always refresh the container. If the SubImage() picture's Handle property
' value is same as the picture passed to that method, refreshing is usually required. When this
' class changes the frame/page of a picture, it attempts to re-use the previous frame/page and simply
' update the pixel data. This may not trigger a change in picture to VB, thus refreshing may be needed.
' The term 'managed', within this class, indicates that the class is rendering the image. This class will
' render the 32bpp bitmaps it creates and also 32bpp icons since VB does not do these. When the picture
' is eventually set to Nothing or replaced by another image, this class will be informed via the thunks
' it created and will release any cached data at that time and dispose of any related GDI+ image.
' In order to support alpha channel image formats, any returned stdPicture object may contain an
' alpha-blended bitmap having its bits premultiplied for use with AlphaBlend API. That API is used to
' render semi-transparency in bitmaps. When the class' IsManaged property returns true, then if the
' Picture.Type value is vbPicTypeBitmap, the bitmap bits are premultiplied. Because of this bitmap
' format, you should not pass the stdPicture outside of your project. The receiving end may not know
' how to render the image. If needed, you can call the StdPictureEx.SavePicture method and convert
' the bitmap to a standard bitmap. Any transparency in the bitmap will be rendered over a background
' color you provided to SavePicture. That method, in that case, always produces a 24bpp bitmap.
' When possible, the cached data is done so that VB also has access to it via the stdPicture itself.
' This applies to non-CMYK JPGs, single-frame GIFs, many icons and most bitmaps. For all other cases,
' the cached data is stored by this class and is not accessible by VB. The StdPictureEx.SavePicture
' method can be used to save this cached data to file. Bitmap and metafile formats are not cached.
' General rule of thumb. If caching original data is wanted and VB can load the image without color
' loss, then the image will be loaded via GDI+ and data will be cached by VB/COM. If this cannot be
' done, then this class will cache the data and manage the picture so the class will know when to
' dispose of the cached data.
' The class creates two assembly thunks (executable code in memory). These thunks are only used to
' prevent crashes, during IDE, when subclassing VB picture objects. Their purposes are briefly
' described next. By existing in memory unknown to VB, the thunks are not destroyed when your project
' closes due to executing an End statement via code, IDE toolbar button or debug message box. Since
' the thunk isn't released in that scenario, its code remains alive, crashes are prevented unlike
' standard subclassing.
'--------------------------------------------------------------------------------------------------------
' Thunk #1: Management Window. This window is created the first time this class loads and remains alive
' until the project or IDE completely unloads. The thunk is the window's subclassed window procedure.
' When the window is destroyed (when project/VB closes), then it properly shuts down GDI+ after
' first releasing any GDI+ image objects that may have been created. It releases Thunk#2 and the
' copy of the COM IPicture/IPictureDisp virtual table. The closure of the project's thread releases
' this thunk.
' Thunk #2: IPicture/IPictureDisp subclasser. This is also only created once. Whenever pictures are
' subclassed, just 4 of the many picture functions are subclassed, the others are untouched. The
' thunk will do the drawing of any subclassed pictures. It also informs this class when a subclassed
' picture is eventually destroyed. The four picture functions that are subclassed/tweaked are:
' 1. IPictureDisp's IUnknown:Release. Tracks when picture is set to nothing or replaced
' 2. IPicture's IUnknown:Release. Tracks when picture is set to nothing or replaced
' 3. IPicture:Get_Attributes. Forces VB to see our subclassed image as transparent. Why?
' VB will only refresh/redraw the area behind the image if it believes the image has
' transparency. By default, only GIFs are recognized 'bitmap' formats that support
' transparency. Subclassing this function/property ensures we can fool VB into believing
' whatever we want has transparency.
' 4. IPicture:Render. Uses AlphaBlend API to draw bitmaps and DrawIconEx API to draw
' icons,cursors.
' FYI: Reason both IUnknown:Release functions are subclassed is that when a picture is set to
' nothing or is replaced or goes out of scope, COM does not expose the zero-reference count in
' both interfaces. It does this in one or the other. So both are tracked to ensure we know when
' it reaches zero.
' VTable Copy: COM appears to handle all pictures via a single interface instance within the current
' thread. All created pictures are processed by this single VTable. To subclass the VTable, this
' class makes a copy of that VTable, then tweaks that copy. All subclassed pictures are rerouted
' to this tweaked VTable. All other picture objects are left untouched. Reversing this can be done
' via the UnManage method.
'========================================================================================================
Option Explicit
'///// GDI functions
Private Declare Function GetDeviceCaps Lib "gdi32.dll" (ByVal hDC As Long, ByVal nIndex As Long) As Long
Private Declare Function GetObjectA Lib "gdi32.dll" (ByVal hObject As Long, ByVal nCount As Long, ByRef lpObject As Any) As Long
Private Declare Function DeleteObject Lib "gdi32.dll" (ByVal hObject As Long) As Long
Private Declare Function DeleteDC Lib "gdi32.dll" (ByVal hDC As Long) As Long
Private Declare Function SelectObject Lib "gdi32.dll" (ByVal hDC As Long, ByVal hObject As Long) As Long
Private Declare Function CreateDIBSection Lib "gdi32.dll" (ByVal hDC As Long, ByRef pBitmapInfo As Any, ByVal un As Long, ByRef lplpVoid As Long, ByVal Handle As Long, ByVal dw As Long) As Long
Private Declare Function SetDIBits Lib "gdi32.dll" (ByVal hDC As Long, ByVal hBitmap As Long, ByVal nStartScan As Long, ByVal nNumScans As Long, ByRef lpBits As Any, ByRef lpBI As Any, ByVal wUsage As Long) As Long
Private Declare Function GetDIBits Lib "gdi32.dll" (ByVal hDC As Long, ByVal hBitmap As Long, ByVal nStartScan As Long, ByVal nNumScans As Long, ByRef lpBits As Any, ByRef lpBI As Any, ByVal wUsage As Long) As Long
Private Declare Function CreateCompatibleDC Lib "gdi32.dll" (ByVal hDC As Long) As Long
Private Declare Function CreateSolidBrush Lib "gdi32.dll" (ByVal crColor As Long) As Long
Private Declare Function DeleteEnhMetaFile Lib "gdi32.dll" (ByVal hEMF As Long) As Long
'///// User32 functions
Private Declare Function GetIconInfo Lib "user32.dll" (ByVal hIcon As Long, ByRef piconinfo As Any) As Long
Private Declare Function GetSysColor Lib "user32.dll" (ByVal nIndex As Long) As Long
Private Declare Function FillRect Lib "user32.dll" (ByVal hDC As Long, ByRef lpRect As RECTI, ByVal hBrush As Long) As Long
Private Declare Function FindWindow Lib "user32.dll" Alias "FindWindowA" (ByVal lpClassName As String, ByVal lpWindowName As String) As Long
Private Declare Function GetWindowThreadProcessId Lib "user32.dll" (ByVal hWnd As Long, ByRef lpdwProcessId As Long) As Long
Private Declare Function GetWindow Lib "user32.dll" (ByVal hWnd As Long, ByVal wCmd As Long) As Long
Private Declare Function FindWindowEx Lib "user32.dll" Alias "FindWindowExA" (ByVal hWndParent As Long, ByVal hWndChildAfter As Long, ByVal lpszClass As String, ByVal lpszWindow As String) As Long
Private Declare Function SetProp Lib "user32.dll" Alias "SetPropA" (ByVal hWnd As Long, ByVal lpString As String, ByVal hData As Long) As Long
Private Declare Function GetProp Lib "user32.dll" Alias "GetPropA" (ByVal hWnd As Long, ByVal lpString As String) As Long
Private Declare Function CreateWindowEx Lib "user32.dll" Alias "CreateWindowExA" (ByVal dwExStyle As Long, ByVal lpClassName As String, ByVal lpWindowName As String, ByVal dwStyle As Long, ByVal x As Long, ByVal y As Long, ByVal nWidth As Long, ByVal nHeight As Long, ByVal hWndParent As Long, ByVal hMenu As Long, ByVal hInstance As Long, ByRef lpParam As Any) As Long
Private Declare Function CopyImage Lib "user32.dll" (ByVal Handle As Long, ByVal un1 As Long, ByVal n1 As Long, ByVal n2 As Long, ByVal un2 As Long) As Long
Private Declare Function GetWindowLong Lib "user32.dll" Alias "GetWindowLongA" (ByVal hWnd As Long, ByVal nIndex As Long) As Long
Private Declare Function SetWindowLong Lib "user32.dll" Alias "SetWindowLongA" (ByVal hWnd As Long, ByVal nIndex As Long, ByVal dwNewLong As Long) As Long
Private Declare Function DestroyIcon Lib "user32.dll" (ByVal hIcon As Long) As Long
Private Declare Function DestroyCursor Lib "user32.dll" (ByVal hCursor As Long) As Long
Private Declare Function GetSystemMetrics Lib "user32.dll" (ByVal nIndex As Long) As Long
Private Declare Function CreateIconFromResourceEx Lib "user32.dll" (ByRef presbits As Any, ByVal dwResSize As Long, ByVal fIcon As Long, ByVal dwVer As Long, ByVal cxDesired As Long, ByVal cyDesired As Long, ByVal flags As Long) As Long
Private Const LR_CREATEDIBSECTION As Long = &H2000
'///// Kernel32 functions
Private Declare Function FreeLibrary Lib "kernel32.dll" (ByVal hLibModule As Long) As Long
Private Declare Function VirtualFree Lib "kernel32.dll" (ByRef lpAddress As Any, ByVal dwSize As Long, ByVal dwFreeType As Long) As Long
Private Declare Function VirtualQuery Lib "kernel32.dll" (ByRef lpAddress As Any, ByRef lpBuffer As Any, ByVal dwLength As Long) As Long
Private Declare Sub FillMemory Lib "kernel32.dll" Alias "RtlFillMemory" (ByRef Destination As Any, ByVal Length As Long, ByVal Fill As Byte)
Private Declare Function GlobalAlloc Lib "kernel32.dll" (ByVal uFlags As Long, ByVal dwBytes As Long) As Long
Private Declare Function GlobalFree Lib "kernel32.dll" (ByVal hMem As Long) As Long
Private Declare Function GlobalLock Lib "kernel32.dll" (ByVal hMem As Long) As Long
Private Declare Function GlobalSize Lib "kernel32.dll" (ByVal hMem As Long) As Long
Private Declare Function GlobalUnlock Lib "kernel32.dll" (ByVal hMem As Long) As Long
Private Declare Function ReadFile Lib "kernel32.dll" (ByVal hFile As Long, ByRef lpBuffer As Any, ByVal nNumberOfBytesToRead As Long, ByRef lpNumberOfBytesRead As Long, ByRef lpOverlapped As Any) As Long
Private Declare Function WriteFile Lib "kernel32.dll" (ByVal hFile As Long, lpBuffer As Any, ByVal nNumberOfBytesToWrite As Long, lpNumberOfBytesWritten As Long, lpOverlapped As Any) As Long
Private Declare Function CreateFileW Lib "kernel32.dll" (ByVal lpFileName As Long, ByVal dwDesiredAccess As Long, ByVal dwShareMode As Long, ByVal lpSecurityAttributes As Long, ByVal dwCreationDisposition As Long, ByVal dwFlagsAndAttributes As Long, ByVal hTemplateFile As Long) As Long
Private Declare Function CloseHandle Lib "kernel32.dll" (ByVal hObject As Long) As Long
Private Declare Function SetFilePointer Lib "kernel32.dll" (ByVal hFile As Long, ByVal lDistanceToMove As Long, ByRef lpDistanceToMoveHigh As Long, ByVal dwMoveMethod As Long) As Long
Private Declare Function GetFileSize Lib "kernel32.dll" (ByVal hFile As Long, ByRef lpFileSizeHigh As Long) As Long
Private Declare Function IsBadCodePtr Lib "kernel32.dll" (ByVal lpfn As Long) As Long
Private Declare Sub CopyMemory Lib "kernel32.dll" Alias "RtlMoveMemory" (ByRef Destination As Any, ByRef Source As Any, ByVal Length As Long)
Private Declare Function GetProcAddress Lib "kernel32.dll" (ByVal hModule As Long, ByVal lpProcName As String) As Long
Private Declare Function LoadLibrary Lib "kernel32.dll" Alias "LoadLibraryA" (ByVal lpLibFileName As String) As Long
Private Declare Function GetModuleHandle Lib "kernel32.dll" Alias "GetModuleHandleA" (ByVal lpModuleName As String) As Long
Private Declare Function VirtualAlloc Lib "kernel32.dll" (ByVal lpAddress As Any, ByVal dwSize As Long, ByVal flAllocationType As Long, ByVal flProtect As Long) As Long
'///// GDI+ functions
Private Declare Function GdiplusStartup Lib "GdiPlus.dll" (Token As Long, inputbuf As Any, Optional ByVal outputbuf As Long = 0) As Long
Private Declare Sub GdiplusShutdown Lib "GdiPlus.dll" (ByVal Token As Long)
Private Declare Function GdipGetImageType Lib "GdiPlus.dll" (ByVal Image As Long, pType As Long) As Long
Private Declare Function GdipDisposeImage Lib "GdiPlus.dll" (ByVal Image As Long) As Long
Private Declare Function GdipLoadImageFromStream Lib "GdiPlus.dll" (ByVal Stream As Long, Image As Long) As Long
Private Declare Function GdipBitmapLockBits Lib "GdiPlus.dll" (ByVal mBitmap As Long, ByRef mRect As RECTI, ByVal mFlags As Long, ByVal mPixelFormat As Long, ByRef mLockedBitmapData As BitmapData) As Long
Private Declare Function GdipBitmapUnlockBits Lib "GdiPlus.dll" (ByVal mBitmap As Long, ByRef mLockedBitmapData As BitmapData) As Long
Private Declare Function GdipCreateBitmapFromScan0 Lib "GdiPlus.dll" (ByVal Width As Long, ByVal Height As Long, ByVal stride As Long, ByVal PixelFormat As Long, scan0 As Any, BITMAP As Long) As Long
Private Declare Function GdipGetImageBounds Lib "GdiPlus.dll" (ByVal nImage As Long, srcRect As RECTF, srcUnit As Long) As Long
Private Declare Function GdipGetImagePixelFormat Lib "GdiPlus.dll" (ByVal hImage As Long, PixelFormat As Long) As Long
Private Declare Function GdipImageSelectActiveFrame Lib "GdiPlus.dll" (ByVal Image As Long, ByRef dimensionID As Any, ByVal FrameIndex As Long) As Long
Private Declare Function GdipImageGetFrameCount Lib "GdiPlus.dll" (ByVal Image As Long, ByRef dimensionID As Any, ByRef count As Long) As Long
Private Declare Function GdipImageGetFrameDimensionsCount Lib "GdiPlus.dll" (ByVal pImage As Long, ByRef count As Long) As Long
Private Declare Function GdipGetImageRawFormat Lib "GdiPlus.dll" (ByVal hImage As Long, ByVal GUID As Long) As Long
Private Declare Function GdipImageGetFrameDimensionsList Lib "GdiPlus.dll" (ByVal pImage As Long, ByRef dimensionIDs As Any, ByVal count As Long) As Long
Private Declare Function GdipEmfToWmfBits Lib "GdiPlus.dll" (ByVal hEMF As Long, ByVal cbData16 As Long, ByVal pData16 As Long, ByVal iMapMode As Long, ByVal eFlags As Long) As Long
Private Declare Function GdipGetHemfFromMetafile Lib "GdiPlus.dll" (ByVal metafile As Long, ByRef hEMF As Long) As Long
Private Declare Function GdipGetPropertyItemSize Lib "GdiPlus.dll" (ByVal pImage As Long, ByVal propId As Long, ByRef pSize As Long) As Long
Private Declare Function GdipGetPropertyItem Lib "GdiPlus.dll" (ByVal pImage As Long, ByVal propId As Long, ByVal propSize As Long, ByRef buffer As Any) As Long
Private Const ImageLockModeUserInputBuf As Long = &H4&
Private Const ImageLockModeRead As Long = &H1&
Private Const PixelFormat32bppPremultiplied As Long = &HE200B
Private Const PixelFormat32bppAlpha As Long = &H26200A
Private Const PixelFormat32bpp As Long = &H262000
Private Const PixelFormat24bpp As Long = &H21808
'///// Misc functions
Private Declare Function OleLoadPicture Lib "OLEPRO32.DLL" (pStream As Any, ByVal lSize As Long, ByVal fRunmode As Long, riid As Any, ppvObj As Any) As Long
Private Declare Function OleCreatePictureIndirect Lib "OLEPRO32.DLL" (lpPictDesc As Any, riid As Any, ByVal fPictureOwnsHandle As Long, iPic As IPicture) As Long
Private Declare Function CreateStreamOnHGlobal Lib "ole32.dll" (ByVal hGlobal As Long, ByVal fDeleteOnRelease As Long, ppstm As Any) As Long
Private Declare Function StringFromGUID2 Lib "ole32.dll" (ByVal rguid As Long, ByVal lpsz As Long, ByVal cchMax As Long) As Long
Private Declare Function IIDFromString Lib "ole32.dll" (ByVal lpsz As Long, ByRef lpiid As Any) As Long
Private Declare Function GetHGlobalFromStream Lib "ole32.dll" (ByVal ppstm As Long, hGlobal As Long) As Long
Private Declare Function CoTaskMemRealloc Lib "ole32.dll" (ByVal pv As Long, ByVal cb As Long) As Long
Private Declare Function GetFileVersionInfo Lib "Version.dll" Alias "GetFileVersionInfoA" (ByVal lptstrFilename As String, ByVal dwhandle As Long, ByVal dwlen As Long, lpData As Any) As Long
Private Declare Function GetFileVersionInfoSize Lib "Version.dll" Alias "GetFileVersionInfoSizeA" (ByVal lptstrFilename As String, lpdwHandle As Long) As Long
Private Declare Function VerQueryValue Lib "Version.dll" Alias "VerQueryValueA" (pBlock As Any, ByVal lpSubBlock As String, lplpBuffer As Any, puLen As Long) As Long
Private Declare Function ImageList_GetIconSize Lib "Comctl32.dll" (ByVal hIML As Long, Cx As Long, Cy As Long) As Long
Private Declare Function SHGetImageListXP Lib "Shell32.dll" Alias "#727" (ByVal iImageList As Long, ByRef riid As Long, ByVal ppv As Long) As Long
Private Declare Function DispCallFunc Lib "oleaut32.dll" (ByVal pvInstance As Long, ByVal offsetinVft As Long, ByVal CallConv As Long, ByVal retTYP As Integer, ByVal paCNT As Long, ByVal paTypes As Long, ByVal paValues As Long, ByRef retVAR As Variant) As Long
Private Type RECTF ' GDI+ rectangle w/Single vartypes
nLeft As Single
nTop As Single
nWidth As Single
nHeight As Single
End Type
Private Type RECTI ' GDI/GDI+ rectangle w/Long vartypes
nLeft As Long
nTop As Long
nWidth As Long
nHeight As Long
End Type
Private Type BitmapData ' GDI+ lock/unlock bits structure
Width As Long
Height As Long
stride As Long
PixelFormat As Long
Scan0Ptr As Long
ReservedPtr As Long
End Type
Private Type ICONDIR
idReserved As Integer ' per msdn: must be zero
idType As Integer ' per msdn: must be 1 or 2
idCount As Integer ' unsigned
End Type
Private Type ICONDIRENTRY
bWidth As Byte ' will be 0 when width > 255
bHeight As Byte ' will be 0 when height > 255
bColorCount As Byte ' not used in this class; not applicable if > 8bpp
bReserved As Byte
wPlanes As Integer
wBitCount As Integer ' not used in this class; extracted from image data instead
dwBytesInRes As Long ' how many bytes are used by the image data
dwImageOffset As Long ' where in icon resource, image data begins (bmp/png header)
End Type
Private Type BITMAPV5HEADER
biSize As Long
biWidth As Long
biHeight As Long
biPlanes As Integer
biBitCount As Integer
' up to this point: BITMAPCOREHEADER 12 bytes (biWidth/biHeight INTEGER vs LONG, bitcount=1,4,8,24 only)
biCompression As Long
biSizeImage As Long
biXPelsPerMeter As Long
biYPelsPerMeter As Long
biClrUsed As Long
biClrImportant As Long
' v2 starts next(52 bytes)
bV2RedMask As Long ' offset 40
bV2GreenMask As Long
bV2BlueMask As Long
' v3 starts next (56 bytes)
bV3AlphaMask As Long ' offset 52
' v4 starts next (108 bytes)
bV4CSType As Long
bV4Endpoints(0 To 8) As Long ' offset 60 << actually bv4Endpoints declared as CIEXYZTRIPLE
bV4GammaRed As Long
bV4GammaGreen As Long
bV4GammaBlue As Long
' v5 starts next (124 bytes)
bV5Intent As Long ' offset 108
bV5ProfileData As Long ' offset 112
bV5ProfileSize As Long ' offset 116
bV5Reserved As Long
End Type
Private Type PICREF ' Used only to track GDI+ image creation/disposal
flags As Long
' 0x0000000F ' PictureTypeConstantsEx value
' 0x0000FFF0 ' current frame (GIF/TIF)
' 0x0FFF0000 ' current frame count (GIF/TIF), max 4095
' 0xF0000000 ' reserved
pIPicture As Long ' ObjPtr(IPicture)
pIPicDisp As Long ' ObjPtr(IPictureDisp)
pHandle As Long ' GDI+ image handle reference (multi-frame/page images only)
oStream As IUnknown ' Cached data (IStream). Optional
End Type
Public Enum PictureTypeConstantsEx ' Used as return value for PictureType property
ptcNone = vbPicTypeNone
ptcBitmap = vbPicTypeBitmap ' BMP & if original format not cached: converted TIF,JPG,PNG,GIF
ptcIcon = vbPicTypeIcon ' VB does not distinguish btwn icon and cursor
ptcMetafile = vbPicTypeMetafile
ptcEMetafile = vbPicTypeEMetafile
ptcJPEG = vbPicTypeEMetafile + 1&
ptcPNG = vbPicTypeEMetafile + 2&
ptcGIF = vbPicTypeEMetafile + 3& ' frame count/navigation available. See LoadPicture method
ptcTIF = vbPicTypeEMetafile + 4& ' page count/navigation available. See LoadPicture method
End Enum
Public Enum LoadPictureSizeConstantsEx
lpsSmall = vbLPSmall ' system small icon size, 16x16
lpsLarge = vbLPLarge ' system large icon size, usually 32x32
lpsSmallShell = vbLPSmallShell ' shell small icon size, usually 16x16 (DPI scalale)
lpsLargeShell = vbLPLargeShell ' shell large icon size, usually 32x32 (DPI scalable)
lpsCustom = vbLPCustom ' user-defined size. LoadPicture DesiredIconCx/Cy parameters apply
lpsDefault = -1& ' if just 1 icon exists, actual size else lpsLarge
lpsXtraLargeShell = -2& ' shell extra large size, usually 48x48 (DPI scalable) XP+
lpsJumboShell = -3& ' shell jump size, 256x256 (Vista+)
End Enum ' note: if shell sizes fail to be retrieved, above sizes are used instead
Private m_PageGUID(0 To 3, 0 To 1) As Long ' used to change frames/pages of GIF/TIF
Private m_hDC As Long ' general usage. Is zero if class failed to initialize properly
Private m_RefCount As Long ' number of m_PicRef() items
Private m_PicRefs() As PICREF ' collection of managed picture data (-1 LBound)
Private m_Primary As StdPictureEx ' subclassing class instance if not this class instance
Private m_Hwnd As Long ' management window handle
Private m_ThunkPtr As Long ' address of IPicture/IPictureDisp subclassing thunk
Private m_VistaPlus As Boolean ' are we running in Vista or better?
Public Function LoadPicture(Optional ImageSource As Variant, _
Optional ByVal IconSize As LoadPictureSizeConstantsEx = lpsDefault, _
Optional ByVal IconColorDepth As Long, _
Optional ByVal DesiredIconCx As Long, _
Optional ByVal DesiredIconCy As Long, _
Optional ByVal KeepOriginalFormat As Boolean = False, _
Optional ByVal RequiredFormat As PictureTypeConstants = vbPicTypeNone) As StdPicture
'========================================================================================================
' Key notes. Method replicates VB's LoadPicture function and adds more options
'========================================================================================================
' Returned Picture object is always a new object, never a reference to source, with one exception.
' If ImageSource is a stdPicture object and this method fails, then it will return a reference to
' the passed stdPicture vs. Nothing.
' When assigning returned picture to image control, you should refresh image control.
' Unicode file names are supported.
' GDI+ is used to support TIF, PNG, CMYK JPGs, non-placeable WMF, and GIF.
' note: Different versions of GDI+ support different TIF compression schemes. Therefore, a TIF loaded
' on Win7 may fail to load on Vista or XP.
'========================================================================================================
'========================================================================================================
' Parameters
'========================================================================================================
' ImageSource. One of the following can be provided. Anything else returns Nothing
' Path/file name of image, as a String. Unicode supported
' Byte array of complete image format/data, one dimensional only
' Existing stdPicture/IPictureDisp object (basically any VB icon/picture property).
' A copy of the passed Picture's data/format will be used. You should not pass that picture
' unless there is a need to duplicate it, waste of system resources. Here are some reasons
' to pass an existing Picture object:
' 1. It is unmanaged and contains a 32bpp alpha bitmap and transparency is wanted
' 2. You want to return the current frame/page of a mutli-frame/page GIF/TIF
' 3. You want a copy of the passed Picture and its cached data
' So, after what was just said, this is what you can expect if passing a stdPicture object:
' - Managed, multi-Page/Frame GIF/TIF.
' If KeepOriginalFormat=True then a copy else single frame/page
' - Any other scenario returns a copy of image & cached data if KeepOriginalFormat=True
'
' IconSize. Applies if loading icons,cursors or if RequiredFormat is vbPicTypeIcon
' lpsDefault. If only 1 icon exists in ImageSource, it is uses as-is else a lpsLarge is the size used.
' lpsLarge. Uses the system's large icon size, usually 32x32 but can be larger
' lpsSmall. Uses the system's small icon size, currently fixes at 16x16
' lpsSmallShell. Windows shell small icon size, usually 16x16 (scales with DPI)
' lpsLargeShell. Windows shell large icon size, usually 32x32 (scales with DPI)
' lpsExtraLargeShell. Windows shell extra large icon size, usually 48x48 (scales with DPI) (XP or better)
' lpsJumboShell. Windows shell jumbo icon size, currently fixed at 256x256 (Vista or better)
' lpsCustom. The DesiredCx,DesiredCy parameters are used else always ignored
'
' IconColorDepth. Applies if loading icons,cursors or if RequiredFormat is vbPicTypeIcon
' 0 = use best color depth that matches the system's color depth
' 1,4,8,16,24,32 = specific color depth. Next highest is used if no match is found
'
' DesiredIconCx,DesireIconCy. Applies if IconSize is lpsCustom and if ImageSource is icon,cursor
' 0 = use system's large icon size
' anything else will result in the icon matching the size being used else resized as needed
'
' KeepOriginalFormat. Applies to all image formats except BMP, WMF, EMF
' Setting to True requires more memory to cache the format. Cached data is always the bits.
' Caching original format data allows StdPictureEx.SavePicture to save the original data
' Tip: do not set this to true if loading from a resource file unless animating a GIF
' or navigating multipage TIF. You have it cached in resource file, no need to re-cache.
' If needed, you can remove the cached data by calling this method again & set this parameter to false
' WMF,EMF,BMP: Ignored. VB/COM can save to these formats without caching original format
' ICO,CUR: VB can save to icon format, but reduces to 4bpp, degrading quality, therefore parameter applies.
' If more than 1 icon/cursor exists in passed data, only the selected image data is cached, not all.
' GIF,TIF: If multiple frames/pages exists, setting parameter to true allows selection of the frames/pages
' If this parameter is False, the only image formats returned are: BMP,ICO,WMF,EMF
' If this parameter is True, then PictureProperty of this class can return: BMP,ICO,WMF,EMF,JPG,PNG,TIF,GIF
'
' RequiredFormat. This option forces the returned picture to one of two formats
' Only vbPicTypeBitmap and vbPicTypeIcon are accepted. KeepOriginalFormat does not apply if bitmap
' is chosen. All parameters apply if icon is chosen This can be useful to force any loaded image to
' icon and simultaneously resize the result by supplying the icon-related size parameters. Most VB
' image properties accept icons but most icon properties do not accept bitmaps
'========================================================================================================
Dim pPicRef As PICREF, newPic As IPictureDisp, bKeepData As Boolean
Call pvVerifyInitialization
' note:if above call fails, then this class cannot be used. Actions default to VB methods only
' if failure, then these variables will contain following values & may be tested elsewhere:
' m_Primary is Nothing, m_hDC = 0, m_ThunkPtr = 0 or -1
If m_Primary Is Nothing Then
Select Case RequiredFormat
Case vbPicTypeIcon ' prevent caching original data until icon is created
bKeepData = KeepOriginalFormat: KeepOriginalFormat = False
Case vbPicTypeBitmap ' bitmaps do not cache original data
KeepOriginalFormat = False
Case vbPicTypeNone
Case Else: RequiredFormat = vbPicTypeNone
End Select
If DesiredIconCx < 0& Then DesiredIconCx = 0& Else DesiredIconCx = DesiredIconCx And &HFFF&
If DesiredIconCy < 0& Then DesiredIconCy = 0& Else DesiredIconCy = DesiredIconCy And &HFFF&
If IconColorDepth < 0& Then IconColorDepth = 0&
If IsMissing(ImageSource) = False Then
If IsEmpty(ImageSource) = False Then
If IsObject(ImageSource) Then
If Not ImageSource Is Nothing Then
If TypeOf ImageSource Is StdPicture Then
If Not ImageSource.Handle = 0& Then ' send off to be processed
Set newPic = ImageSource
Set newPic = pvLoadStdPicture(newPic, IconSize, DesiredIconCx, DesiredIconCy, _
KeepOriginalFormat, pPicRef)
If newPic Is Nothing Then Set newPic = ImageSource
End If
End If
End If
ElseIf VarType(ImageSource) = vbString Then
If Not ImageSource = vbNullString Then ' send off to be processed
Set newPic = pvLoadFile(CStr(ImageSource), IconSize, IconColorDepth, DesiredIconCx, DesiredIconCy, _
KeepOriginalFormat, pPicRef)
End If
ElseIf VarType(ImageSource) = (vbArray Or vbByte) Then
Dim iData() As Byte
On Error Resume Next
pPicRef.flags = LBound(ImageSource, 2)
If Err Then ' not multi-dimensional array
Err.Clear ' check for uninitialized/invalid arrays
If UBound(ImageSource) > LBound(ImageSource) Then
If Err.Number = 0& Then ' send off to be processed
On Error GoTo 0
iData() = ImageSource
Set newPic = pvLoadPicture(iData(), IconSize, IconColorDepth, DesiredIconCx, DesiredIconCy, _
KeepOriginalFormat, pPicRef)
End If
End If
End If
End If
End If
End If
Else
Set LoadPicture = m_Primary.LoadPicture(ImageSource)
End If
If Not newPic Is Nothing Then
If RequiredFormat = vbPicTypeBitmap Then
Set newPic = pvCvAnytoBMP(newPic, pPicRef)
ElseIf RequiredFormat = vbPicTypeIcon Then
Set newPic = pvCvAnyToICO(newPic, pPicRef, bKeepData, IconSize, DesiredIconCx, DesiredIconCy)
End If
' when above pvLoad[xxx] calls return, the pPicRef.Flags is non-null if class will manage it
If pPicRef.flags Then
Dim nPic As IPicture
Set nPic = newPic: pPicRef.pIPicture = ObjPtr(nPic): Set nPic = Nothing
pPicRef.pIPicDisp = ObjPtr(newPic)
Call pvAddGDIpItem(pPicRef)
CopyMemory ByVal pPicRef.pIPicDisp, GetProp(m_Hwnd, "IPicDispAddr"), 4&
CopyMemory ByVal pPicRef.pIPicture, GetProp(m_Hwnd, "IPicAddr"), 4&
End If
Set LoadPicture = newPic
End If
End Function
Public Sub SavePicture(Picture As StdPicture, Destination As Variant, _
Optional ByVal AlwaysSaveAsBitmap As Boolean = False, _
Optional ByVal KeepPremultipliedBits As Boolean, _
Optional ByVal BitmapBkgColor As OLE_COLOR = vbWindowBackground)
'========================================================================================================
' Key notes. Method replicates VB's SavePicture function and adds more options
' Unicode file names are supported
' This function does no conversion between image formats, other than converting to bitmap as needed
' If class is not initialized completely, results will be no worse than VB's SavePicture method
'========================================================================================================
'========================================================================================================
' Parameters
'========================================================================================================
' Picture: Image to save
' Destination can be one of these. Anything else cause routine to abort/fail
' 1) Full path/filename to save to. Unicode supported.
' You should call this class' PictureType property to determine image format for file extension
' 2) A byte array. The entire image format would be returned in that array
' When method returns successfully, array can be passed to LoadPicture if desired
' 3) A stdPicture object, i.e., Dim myPic As New stdPicture. Copy is always returned
' AlwaysSaveAsBitmap will force image format to 24 or 32 bit bitmap
' KeepPremultipliedBits will save to 32bpp bitmap if the passed Picture
' contains a 32bpp premultiplied bitmap
' BitmapBkgColor is used if saving images with transparency to bitmap format.
' VB system color constants are accepted, i.e., vbWindowBackground
'========================================================================================================
Call pvVerifyInitialization
If m_Primary Is Nothing Then
If Picture Is Nothing Then Exit Sub
If Picture.Handle = 0& Then Exit Sub
Dim dFormat As Long, hFile As Long, icData() As Byte
Dim hDIB As Long, hBits As Long, lSize As Long, lErr As Long
Dim BIH As BITMAPV5HEADER, brRect As RECTI, p As PICREF
Dim Index As Long, lWritten As Long, oIPic As IPicture
Const FILE_ATTRIBUTE_NORMAL = &H80&
Const GENERIC_READ As Long = &H80000000
Const GENERIC_WRITE As Long = &H40000000
Const CREATE_ALWAYS As Long = 2&
If VarType(Destination) = vbString Then ' sanity checks
If Destination = vbNullString Then Exit Sub ' attempt to open new file
hFile = CreateFileW(StrPtr(CStr(Destination)), GENERIC_READ Or GENERIC_WRITE, 0&, 0&, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0&)
If hFile = -1& Then GoTo ExitRoutine
p.flags = 1&
ElseIf VarType(Destination) = (vbByte Or vbArray) Then
p.flags = 2&
ElseIf IsObject(Destination) Then
If TypeOf Destination Is StdPicture Then p.flags = 3&
End If
If p.flags = 0& Then lErr = 17&: GoTo ExitRoutine
dFormat = Me.PictureType(Picture) ' get format of Picture
Set oIPic = Picture
If AlwaysSaveAsBitmap Or (dFormat = ptcBitmap And Not oIPic.KeepOriginalFormat) Then
Index = pvPictureToIndex(Picture) ' convert/save to bitmap
With BIH
If Index > -1& And KeepPremultipliedBits = True Then ' managed, keep premultiplied bits?
If Picture.Type = ptcBitmap Then .biBitCount = 32 Else .biBitCount = 24
Else
.biBitCount = 24
End If
.biPlanes = 1: .biSize = 40
.biWidth = (1440! / Screen.TwipsPerPixelX) * Picture.Width / 2540!
.biHeight = (1440! / Screen.TwipsPerPixelY) * Picture.Height / 2540!
.biSizeImage = pvDWordAlign(.biBitCount, .biWidth, .biHeight)
End With ' create bitmap & bail on error
hDIB = CreateDIBSection(m_hDC, BIH, 0&, hBits, 0&, 0&)
If hDIB = 0& Then lErr = 7&: GoTo ExitRoutine
hDIB = SelectObject(m_hDC, hDIB)
If BIH.biBitCount = 24 Then ' fill with bkg color as needed
If BitmapBkgColor < 0& Then BitmapBkgColor = GetSysColor(BitmapBkgColor And &HFF)
p.pHandle = CreateSolidBrush(BitmapBkgColor)
brRect.nHeight = BIH.biHeight: brRect.nWidth = BIH.biWidth
FillRect m_hDC, brRect, p.pHandle: DeleteObject p.pHandle
End If
With Picture ' render to the bitmap
.Render (m_hDC), 0&, 0&, (BIH.biWidth), (BIH.biHeight), 0&, (.Height - 1&), .Width, -.Height, ByVal 0&
End With
hDIB = SelectObject(m_hDC, hDIB)
If p.flags = 3& Then
Set Destination = pvHandleToStdPicture(hDIB, vbPicTypeBitmap)
If Not Destination Is Nothing Then hDIB = 0& ' prevent destruction at end of routine
ElseIf p.flags = 2& Then
ReDim icData(0 To 53& + BIH.biSizeImage)
icData(0) = &H42: icData(1) = &H4D ' build bmp header
CopyMemory icData(2), CLng(BIH.biSizeImage + 54&), 4&
icData(10) = 54
CopyMemory icData(14), BIH, 40& ' copy bitmap
CopyMemory icData(54), ByVal hBits, BIH.biSizeImage
Else ' bail if error, likely out of disk space
WriteFile hFile, &H4D42, 2&, lWritten, ByVal 0& ' build bmp header
If Not lWritten = 2& Then GoTo ExitRoutine
WriteFile hFile, CLng(BIH.biSizeImage + 54&), 4&, lWritten, ByVal 0&
If Not lWritten = BIH.biSizeImage + 54& Then GoTo ExitRoutine
WriteFile hFile, 0&, 4&, lWritten, ByVal 0&
If Not lWritten = 4& Then GoTo ExitRoutine
WriteFile hFile, 54&, 4&, lWritten, ByVal 0&
If Not lWritten = 54& Then GoTo ExitRoutine
WriteFile hFile, BIH, 40&, lWritten, ByVal 0& ' write bitmap
If Not lWritten = 40& Then GoTo ExitRoutine
WriteFile hFile, ByVal hBits, BIH.biSizeImage, lWritten, ByVal 0&
If Not lWritten = BIH.biSizeImage Then GoTo ExitRoutine
End If
ElseIf p.flags = 3& Then
Set Destination = Me.LoadPicture(Picture, , , , True)
Else
dFormat = p.flags ' cache; pvLoadStdPicture resets the value
Call pvLoadStdPicture(Picture, 0&, 0&, 0&, True, p, True)
p.flags = dFormat
If p.oStream Is Nothing Then lErr = 5: GoTo ExitRoutine ' should not happen
GetHGlobalFromStream ObjPtr(p.oStream), p.pHandle
lSize = GlobalSize(p.pHandle)
If lSize = 0& Then lErr = 5: GoTo ExitRoutine
p.pIPicDisp = GlobalLock(p.pHandle)
If p.flags = 2& Then ' save to array
ReDim icData(0 To lSize - 1&)
CopyMemory icData(0), ByVal p.pIPicDisp, lSize
Else ' save to file
WriteFile hFile, ByVal p.pIPicDisp, lSize, lWritten, ByVal 0&
lErr = Err.LastDllError
GlobalUnlock p.pHandle
Set p.oStream = Nothing
End If
End If
Else
Call m_Primary.SavePicture(Picture, Destination, AlwaysSaveAsBitmap, KeepPremultipliedBits, BitmapBkgColor)
Exit Sub
End If
ExitRoutine:
If lErr = 0& Then lErr = Err.LastDllError
Set oIPic = Nothing
If hDIB Then DeleteObject hDIB
If Not (hFile = 0& Or hFile = -1&) Then CloseHandle hFile
If lErr Then
Err.Raise lErr, "SavePicture"
ElseIf p.flags = 2& Then
Destination = icData()
End If
End Sub
Public Property Get SubImageCount(Picture As StdPicture) As Long
' return number of pages/frames of multi-frame/page GIF/TIF
If pvVerifyInitialization Then
If m_Primary Is Nothing Then
SubImageCount = (m_PicRefs(pvPictureToIndex(Picture)).flags And &HFFF0000) \ &H10000
Else
SubImageCount = m_Primary.SubImageCount(Picture)
End If
End If
End Property
Public Property Get SubImageIndex(Picture As StdPicture) As Long
' return current frame/page index of multi-frame/page GIF/TIF
If pvVerifyInitialization Then
If m_Primary Is Nothing Then
SubImageIndex = (m_PicRefs(pvPictureToIndex(Picture)).flags And &HFFF0&) \ &H10
Else
SubImageIndex = m_Primary.SubImageIndex(Picture)
End If
End If
End Property
Public Function SubImage(Picture As StdPicture, ByVal NewIndex As Long) As StdPicture
' return a different frame/page
' NewIndex must be between 1 and SubImageCount
' Upon return, you should refresh the control the picture is assigned to
If pvVerifyInitialization Then
If m_Primary Is Nothing Then
If Not Picture Is Nothing Then
If Picture.Handle = 0& Or NewIndex < 1& Then
Set SubImage = Picture
Else
Dim lValue As Long, Index As Long
Index = pvPictureToIndex(Picture)
If Index > -1& Then
lValue = (m_PicRefs(Index).flags And &HFFF0000) \ &H10000
If Not (lValue < 2& Or NewIndex > lValue) Then
m_PicRefs(Index).flags = (m_PicRefs(Index).flags And &HFFFF000F) Or NewIndex * &H10&
If (m_PicRefs(Index).flags And &HF) = ptcTIF Then
Set SubImage = pvSetPage(Picture, Index)
Else
Call pvSetFrame(Picture, Index)
End If
End If
End If
If SubImage Is Nothing Then Set SubImage = Picture
End If
End If
Else
Set SubImage = m_Primary.SubImage(Picture, NewIndex)
End If
End If
End Function
Public Sub UnManage(Picture As StdPicture)
' provided for troubleshooting/debugging.
' Normally you would not want to call this method. The image will not be rendered correctly
Dim nIPic As IPicture
Dim pTable As Long, vTable As Long, tThunk As Long
If pvVerifyInitialization Then
If m_Primary Is Nothing Then
If Not Picture Is Nothing Then
If Not Picture.Handle = 0& Then
CopyMemory tThunk, ByVal pvSafePointerAdd(m_ThunkPtr, 8&), 4& ' get pointer to VTable thunk
CopyMemory vTable, ByVal pvSafePointerAdd(tThunk, 4&), 4& ' get original VTable address
CopyMemory pTable, ByVal ObjPtr(Picture), 4& ' VTable address for Picture object
If Not (pTable = vTable) Then
CopyMemory ByVal ObjPtr(Picture), vTable, 4&
Set nIPic = Picture
CopyMemory vTable, ByVal pvSafePointerAdd(tThunk, 8&), 4& ' get original VTable address
CopyMemory ByVal ObjPtr(nIPic), vTable, 4&
Set nIPic = Nothing
Call pvReleased(ObjPtr(Picture))
End If
End If
End If
Else
Call m_Primary.UnManage(Picture)
End If
End If
End Sub
Public Property Get IsManaged(Picture As StdPicture) As Boolean
' returns whether VB or this class is rendering/managing the passed Picture
If pvVerifyInitialization Then
If m_Primary Is Nothing Then
If Not Picture Is Nothing Then
If pvPictureToIndex(Picture) > -1& Then
Dim pTable As Long, vTable As Long
CopyMemory pTable, ByVal pvSafePointerAdd(m_ThunkPtr, 8&), 4& ' get pointer to VTable thunk
CopyMemory vTable, ByVal pvSafePointerAdd(pTable, 4&), 4& ' get original VTable address
CopyMemory pTable, ByVal ObjPtr(Picture), 4& ' VTable address for Picture object
IsManaged = Not (pTable = vTable)
End If
End If
Else
IsManaged = m_Primary.IsManaged(Picture)
End If
End If
End Property
Public Property Get PictureType(Picture As StdPicture) As PictureTypeConstantsEx
' replacement for VB's Picture.Type property
' managed images can return GIF, JPG, TIF, PNG as picture types
If pvVerifyInitialization Then
If m_Primary Is Nothing Then
If Not Picture Is Nothing Then
Dim lSize As Long, oPic As IPicture, oStream As IUnknown
Dim hMem As Long, hLock As Long
With m_PicRefs(pvPictureToIndex(Picture))
If .flags = 0 Then ' passed unmanaged Picture
PictureType = Picture.Type ' set default return value
Set oPic = Picture
If oPic.KeepOriginalFormat Then ' can peek to see if GIF or JPG
hMem = GlobalAlloc(&H2&, 0&)
If hMem Then
Call CreateStreamOnHGlobal(hMem, 1&, oStream)
Else
GlobalFree hMem
End If
If Not oStream Is Nothing Then
oPic.SaveAsFile ByVal ObjPtr(oStream), 0&, lSize
If lSize < 4& Then
Set oStream = Nothing
Else
hLock = GlobalLock(hMem)
If hLock Then
CopyMemory lSize, ByVal hLock, 4&
GlobalUnlock hMem
Else
lSize = 0&
End If
If (lSize And &HFFFFFF) = &H464947 Then
PictureType = ptcGIF
ElseIf Not (lSize And &HFFFF&) = &H4D42& Then
PictureType = ptcJPEG
End If
End If
Set oStream = Nothing
End If
End If
Else
PictureType = .flags And &HF
End If
End With
End If
Else
PictureType = m_Primary.PictureType(Picture)
End If
End If
End Property
Public Function GetGIFAnimationInfo(Picture As StdPicture, Durations() As Long, _
Optional ByVal ZeroDurationAdjustment As Long = 50) As Boolean
' Returns Animated GIF frame durations and suggested loop count
' If the function returns false then Durations() array is undefined
' The returned Durations() array will be zero-bound and contain each frame's duration
' Durations(0) = suggested loop count. Infinite, undefined counts are values < 1
' Durations(1 to frameCount) are values scaled back by 0.1, so multiply them by 10
' ZeroDurationAdjustment is user-defined duration if the GIF reports zero for any frame.
' This value cannot be less than 10ms. And setting less than 10 will default to 10
If pvVerifyInitialization = False Then Exit Function
If m_Primary Is Nothing Then
Const PropertyTagFrameDelay As Long = &H5100
Const PropertyTagLoopCount As Long = &H5101
Dim Index As Long, hImage As Long
Dim pData() As Byte, pSize As Long, pPointer As Long
Index = pvPictureToIndex(Picture)
If Index < 0& Then Exit Function
If Not (m_PicRefs(Index).flags And &HF) = ptcGIF Then Exit Function ' not GIF
hImage = m_PicRefs(Index).pHandle
If GdipGetPropertyItemSize(hImage, PropertyTagFrameDelay, pSize) Then Exit Function
ReDim pData(0 To pSize - 1&)
If GdipGetPropertyItem(hImage, PropertyTagFrameDelay, pSize, pData(0)) Then Exit Function
CopyMemory pSize, pData(4), 4&
ReDim Durations(0 To (m_PicRefs(Index).flags And &HFFF0000) \ &H10000)
CopyMemory pPointer, pData(12), 4&
CopyMemory Durations(1), ByVal pPointer, pSize
If GdipGetPropertyItemSize(hImage, PropertyTagLoopCount, pSize) = 0& Then
If pSize < UBound(pData) Then
If GdipGetPropertyItem(hImage, PropertyTagLoopCount, pSize, pData(0)) = 0& Then
CopyMemory pSize, pData(4), 4&
If pSize < 5& Then
CopyMemory pPointer, pData(12), 4&
CopyMemory Durations(0), ByVal pPointer, pSize
End If
End If
End If
End If
If ZeroDurationAdjustment < 10& Then ZeroDurationAdjustment = 10&
ZeroDurationAdjustment = ZeroDurationAdjustment \ 10&
For pSize = 1& To UBound(Durations)
If Durations(pSize) < 1& Then Durations(pSize) = ZeroDurationAdjustment
Next
GetGIFAnimationInfo = True
Else
GetGIFAnimationInfo = m_Primary.GetGIFAnimationInfo(Picture, Durations(), ZeroDurationAdjustment)
End If
End Function
Public Property Get HasOriginalFormat(Picture As StdPicture) As Boolean
' returns whether original image format/data is maintained. If maintained and not needed,
' you can call this class LoadPicture and pass the KeepOriginalFormat parameter as false to
' reload image without caching the data, reducing memory usage, i.e.,
' Set Image1.Picture = StdPictureEx.LoadPicture(Image1.Picture)
' Note: When your project is uncompiled, VB ensures loaded pictures cache original data/format.
' But when compiled, those same Pictures no longer have their data cached. In other words do
' not assume that this property will return the same result for design-time loaded images
' when your project is compiled vs. uncompiled. If original format is needed, highly recommend
' you store your images in a resource file and load them from there.
Dim oIPic As IPicture
Call pvVerifyInitialization
If m_Primary Is Nothing Then
With m_PicRefs(pvPictureToIndex(Picture))
If .oStream Is Nothing Then
Set oIPic = Picture
HasOriginalFormat = CBool(oIPic.KeepOriginalFormat)
Else
HasOriginalFormat = True
End If
End With
Else
HasOriginalFormat = m_Primary.HasOriginalFormat(Picture)
End If
End Property
Private Function pvSetPage(Picture As StdPicture, Index As Long) As StdPicture
' changes the page of a multi-page TIF
Dim goBMP(0 To 5) As Long ' equivalent to Windows BITMAP structure
Dim rBounds As RECTI, uBMP As BitmapData, nIPic As IPicture
Dim hImage As Long, pBounds As RECTF, BIH As BITMAPV5HEADER
Dim hDIB As Long, hBits As Long
With m_PicRefs(Index)
If GdipImageSelectActiveFrame(.pHandle, m_PageGUID(0, 1), (.flags And &HFFF0&) \ &H10 - 1&) Then Exit Function
hImage = .pHandle
End With
GdipGetImageBounds hImage, pBounds, 2& ' 2=UnitPixel
GetObjectA Picture.Handle, 24&, goBMP(0)
With BIH
.biWidth = pBounds.nWidth: .biHeight = pBounds.nHeight
.biBitCount = 32&: .biPlanes = 1: .biSize = 40&
End With
If goBMP(1) = BIH.biWidth And Abs(goBMP(2)) = BIH.biHeight And hBits <> 0& Then
' can reuse the same bitmap?
If goBMP(5) = 0& Then
hDIB = CreateDIBSection(m_hDC, BIH, 0&, hBits, 0&, 0&)
Else
hBits = goBMP(5)
End If
Else
hDIB = CreateDIBSection(m_hDC, BIH, 0&, hBits, 0&, 0&)
End If
If hBits Then
With uBMP
.Width = BIH.biWidth: .Height = BIH.biHeight
rBounds.nHeight = .Height: rBounds.nWidth = .Width
.PixelFormat = PixelFormat32bppPremultiplied
.stride = -.Width * 4&
.Scan0Ptr = pvSafePointerAdd(hBits, (.Height - 1&) * -.stride)
End With
If GdipBitmapLockBits(hImage, rBounds, ImageLockModeRead Or ImageLockModeUserInputBuf, uBMP.PixelFormat, uBMP) Then
If GdipBitmapLockBits(hImage, rBounds, ImageLockModeRead Or ImageLockModeUserInputBuf, uBMP.PixelFormat, uBMP) Then uBMP.Scan0Ptr = 0&
End If
If uBMP.Scan0Ptr = 0& Then
If hDIB Then DeleteObject hDIB
Else
GdipBitmapUnlockBits hImage, uBMP
If hDIB Then ' not using the same image handle, prevent clearing of cached info & GDI+ handle
Set pvSetPage = pvHandleToStdPicture(hDIB, vbPicTypeBitmap)
If pvSetPage Is Nothing Then
DeleteObject hDIB
Else
Set nIPic = pvSetPage
m_PicRefs(Index).pIPicDisp = ObjPtr(pvSetPage)
m_PicRefs(Index).pIPicture = ObjPtr(nIPic)
End If
End If
End If
End If
End Function
Private Sub pvSetFrame(Picture As StdPicture, Index As Long)
' changes the frame of an animated GIF
Dim goBMP(0 To 5) As Long ' equivalent to Windows BITMAP structure
Dim rBounds As RECTI, uBMP As BitmapData
Dim hImage As Long
With m_PicRefs(Index)
If GdipImageSelectActiveFrame(.pHandle, m_PageGUID(0, 0), (.flags And &HFFF0&) \ &H10 - 1&) Then Exit Sub
hImage = .pHandle
End With
GetObjectA Picture.Handle, 24&, goBMP(0)
If Not goBMP(5) = 0& Then ' else no bits pointer
' to change the frame, we simply render over the existing GIF frame
With uBMP
.Width = goBMP(1): .Height = goBMP(2)
.stride = .Width * -4& ' ensure pixels are extracted bottom-up vs. GDI+ top-down
.Scan0Ptr = pvSafePointerAdd(goBMP(5), (.Height - 1&) * -.stride)
.PixelFormat = PixelFormat32bppPremultiplied
rBounds.nHeight = .Height: rBounds.nWidth = .Width
End With
If GdipBitmapLockBits(hImage, rBounds, ImageLockModeRead Or ImageLockModeUserInputBuf, uBMP.PixelFormat, uBMP) Then
If GdipBitmapLockBits(hImage, rBounds, ImageLockModeRead Or ImageLockModeUserInputBuf, uBMP.PixelFormat, uBMP) Then uBMP.Scan0Ptr = 0&
End If
If uBMP.Scan0Ptr Then GdipBitmapUnlockBits hImage, uBMP
End If
End Sub
Private Function pvPictureToIndex(Picture As IPictureDisp) As Long
' returns index for passed managed picture
Dim pObj As Long
If Picture Is Nothing Then
pvPictureToIndex = -1&
Else
pObj = ObjPtr(Picture)
For pvPictureToIndex = m_RefCount - 1& To 0& Step -1&
If m_PicRefs(pvPictureToIndex).pIPicDisp = pObj Then Exit For
Next
End If
End Function
Private Function pvVerifyInitialization() As Boolean
' helper function that ensures a) thunks created and/or b) primary subclasser identified
If Not m_ThunkPtr = -1& Then
If m_ThunkPtr = 0& Then
If pvCreateThunks() = True Then
If m_Primary Is Nothing Then
m_hDC = CreateCompatibleDC(0&)
ReDim m_PicRefs(-1 To -1)
End If
pvVerifyInitialization = True
End If
Else
pvVerifyInitialization = True
End If
End If
End Function
Private Function pvLoadPicture(SourceData() As Byte, Size As Long, IconColorDepth As Long, _
DesiredIconCx As Long, DesiredIconCy As Long, _
KeepFormat As Boolean, _
pPicRef As PICREF) As StdPicture
' workhorse for the class
Dim hBmp As Long, lRead As Long, lFlags As Long
Dim hBits As Long, hImage As Long, hToken As Long, LB As Long
Dim BIH As BITMAPV5HEADER, uBMP As BitmapData, rBounds As RECTI
Dim IStream As IUnknown
Const ImageLockModeWrite = &H2
LB = LBound(SourceData)
If m_hDC Then ' else class not initialized
CopyMemory lFlags, SourceData(LB), 4&
If (lFlags And &HFFFF&) = &H4D42& Then ' bitmaps handled separately to support 32bpp
KeepFormat = False ' and also to support v4 & v5 of the bitmap info header
CopyMemory BIH.biSize, SourceData(LB + 14&), 4& ' size of header
If Not (BIH.biSize < 40& Or BIH.biSize > 124&) Then ' quick validation
CopyMemory BIH.biWidth, SourceData(LB + 18&), 36& ' get next 36 bytes of header
If BIH.biBitCount > 0 And BIH.biCompression < 4& Then ' else likely BI_PNG,BI_JPG & not supported