Access

  MS Access 2010+  |  Bitmapa *.bmp  |   VBA 7.0

• Jak zapisać na dysk 24-bitową bitmapę osadzoną w formancie Image

• Właściwość PictureData formantu Image.

Microsoft Access przechowuje wyświetlaną bitmapę we właściwości obiekt.PictureData. Obiektem może być formularz, raport oraz przycisku polecenia (Button), przycisku przełącznika (ToggleButton) i najważniejszy (jak dla mnie) formant .obraz (Image). Właściwość PictureData jest binarną reprezentacją wyświetlanej bitmapy i jest do odczytu i zapisu we wszystkich widokach. Więcej o właściwości PictureData można znaleźć na stronie tego serwisu Image.PictureData

• 24-bitowa nieskompresowana bitmapa

Wszystkie przykłady dotyczące przetwarzania bitmap, ograniczam do 24-bitowych bitmap Bitmapa taka posiada 14 bajtowy nagłówek BITMAPFILEHEADER, zawierający sygnaturę 'BM', 40 bajtowy nagłówek BITMAPINFOHEADER, liczba bitów koloru na jeden piksel wynosi 24, jest zapisana bez użycia algorytmów kompresujących, a kierunek odczytu bajtów z tablicy bajtów obrazu jest w kierunku  „z dołu do góry”. Bardziej szczegółowe informacje o strukturze bitmapy można znaleźć na stronie Struktura bitmapy

Co opisuje właściwość PictureData?

Właściwość PictureData faktycznie jest tablicą bajtów. Jeszcze do niedawna dla 24-bitowej bitmapy pierwsze 40 bajty tablicy był to nagłówek BitmapInfoHeader, a następne bajty określały kolor składowych RGB kolejnych pikseli bitmapy. Taką tablicę bajtów określa się mianem upakowana DIB, lub skrótowo DIB.

Niestety, w MS Access 2007+ pojawiła się nowa właściwość bazy danych „Format przechowywania właściwości obrazów” i pojęcie „upakowana DIB” straciło swoją jednoznaczność.
„upakowana DIB” zaczyna się 40 bajtowym nagłówkiem BitmaInfoHeader, czyli od 15-go bajtu pliku bitmapy i zawiera wszystkie pozostałe bajty bitmapy.

  • W MS Access 2007+ i wyższych wersjach, dla ustawionej opcji „Konwertuj wszystkie dane obrazu na mapy bitowe” oraz w  wersji MS Access 2003- i niższych właściwość PictureData zawierać będzie „upakowaną DIB”, czyli 40 bajtowy nagłówek BitmaInfoHeader i wszystkie pozostałe bajty określające kolor składowych RGB pikseli bitmapy.
  • dla opcji „Zachowaj format obrazu bitowego”, właściwość PictureData zawierać będzie wszystkie bajty pliku bitmapy, czyli 14 bajtowy nagłówek BitmaFileHeader, 40 bajtowy nagłówek BitmaInfoHeader i wszystkie pozostałe bajty określające kolor składowych RGB pikseli bitmapy.

Na stronie Format właściwości obrazów znajduje się więcej informacji o właściwości „Format przechowywania właściwości obrazów”.

Sposób zapisu bajtów bitmapy we właściwości PictureData

Z przedstawionych powyżej rozważań wynika, że wystarczy pobrać właściwość „Format przechowywania właściwości obrazów” i dla wartości:

0 - „Zachowaj format obrazu źródłowego
powinniśmy zapisać całą tablicę PictureData na dysk, gdyż zawiera ona wszystkie bajty bitmapy (czyli 14 bajtowy nagłówek BitmaFileHeader, 40 bajtowy nagłówek BitmaInfoHeader i bajty obrazu).
1 - „Konwertuj wszystkie dane obrazu na mapy bitowe (zgodne z programem Access 2003- ...)”
tablica PictureData zaczyna się 40 bajtowym nagłówkiem BitmaInfoHeader za którym znajdują się bajty obrazu. Wystarczy utworzyć nagłówek BitmaFileHeader, wypełnić trzy elementy struktury:
  • .bfType=&H4D42, czyli dwa znaki 'BM'
  • .bfSize=CLng(UBound(bBmpArray()) + 40 + 1)
  • .bfOffBits = 14 + 40

i zapisać na dysk. Prawda, jakie to proste (͡° ͜ʖ ͡°).

Proste jest jedynie w przypadku nowej bazy, w której została wybrana jedna z dwóch opcji „Formatu przechowywania właściwości obrazów” i opcja ta nigdy nie zostanie zmieniona. A dlaczego? Wystarczy wykonać prosty test.
W MS Access 2007+ tworzymy nową bazę w formacie *.accdb. Właściwość „Format przechowywania właściwości obrazów” ustawiona jest domyślnie na „Zachowaj format obrazu źródłowego”. W formularzu wstawiamy formant Rysunek (Image), a do niego osadzoną 24-bitową bitmapę. Zdarzeniu „Click” formantu Rysunek (Image) przypisujemy procedurę:

01. Czy tablica typy Byte jest prawidłową tablicą PictureData? Funkcja bmpIsArrayDIB(...)
1
2
3
4
5
6
7
8
9
10
Private Sub img_Click()
Dim aPictData() As Byte
 
  aPictData = Me.img.PictureData
   
  Debug.Print "Storage Format = "; CurrentDb.Properties("Picture Property Storage Format")
  Debug.Print "&H" & Format$(Hex(aPictData(0)), "00") & Format$(Hex(aPictData(1)), "00")
  Debug.Print Chr$(aPictData(0)) & Chr$(aPictData(1))
 
End Sub

i uruchamiamy procedurę Private Sub img_Click(). Następnie w Menu: „Opcje programu Access”/„Bieżąca baza danych”/ „Format przechowywania właściwości obrazów” zmieniamy na opcję „Konwertuj wszystkie dane obrazu na mapy bitowe” (zgodne z programem Access 2003 i wcześniejszymi wersjami) i ponownie uruchamiamy procedurę img_Click(). W oknie „Immediate” otrzymujemy wynik:

Sygnatura bmp

Jak widać, w obu przypadkach w formancie Rysunek (Image) bitmapa zapisana jest w taki sam sposób. Na początku znajduje się 14 bajtowy nagłówek BitmaFileHeader w którym dwa pierwsze bajty to sygnatura pliku 'BM' (&H4D42). Zapis danych jest zgodny z opcją „Zachowaj format obrazu źródłowego”.

Teraz usuwamy bitmapę z formantu Rysunek (Image) i ponownie osadzamy tą samą 24-bitową bitmapę. Uruchamiamy procedurę img_Click(), przywracamy „Format przechowywania właściwości obrazów” na „Zachowaj format obrazu źródłowego” i po raz ostatni uruchamiamy procedurę img_Click(). W oknie „Immediate” otrzymujemy wynik:

Sygnatura bmp

I w tym przypadku dla obu opcji, w formancie Rysunek (Image) bitmapa zapisana jest w taki sam sposób, ale sposób zapisu uległ zmianie. Na początku, znajduje się 40 bajtowy nagłówek BitmaInfoHeader, w którym pierwszy bajt ma wartość &H28, co w zapisie dziesiętnym wynosi 40 i odpowiada wielkości nagłówka BitmaInfoHeader. Przechowywanie obrazu jest zgodne z opcją „Konwertuj wszystkie dane obrazu na mapy bitowe”

Znak Informacja dodatkowa Wniosek jest prosty, MS Access nie konwertuje już osadzonych plików na format zgodny z aktualnie ustawioną (zmienioną) właściwością „Format przechowywania właściwości obrazów”. Zmiana opcji obowiązuje przy osadzaniu nowych obrazów do formantów. Wystarczy wtedy ponownie osadzić ten sam plik w formancie Rysunek (Image), by MS Access dokonał odpowiednich zmian.

Opis funkcji bmpDibToDisc(...) As Boolean

Chcąc zapisać na dysk 24-bitową bitmapę osadzoną w formancie Image, możemy od razu przystąpić do zapisu. Możemy też na wszelki wypadek sprawdzić za pomocą funkcji bmpIsArrayDIB(...), czy rzeczywiście mamy do czynienia z 24-bitową nieskompresowaną bitmapą. Ponieważ pobranie właściwości „Format przechowywania właściwości obrazów” nie umożliwia jednoznacznego określenia sposobu przechowywania bitmapy, sprawdzimy, czy dwa pierwsze bajty tablicy to sygnatura 'BM'. Jeżeli tak, to tablica jest kompletną bitmapą, jeżeli dwa pierwsze bajty tablicy nie są sygnaturą 'BM' to przed zapisem  musimy sami wypełnić elementy struktury BitmaFileHeader.

⊗ Public Function bmpDibToDisc(sDestFullPath As String, bBmpArray() As Byte) As Long
  • Korzystając funkcji plikFileExist(...), sprawdza czy plik istniej na dysku. Jeżeli plik istnieje, to użytkownik jest proszony o zgodę na jego nadpisanie. Następnie za pomocą funkcji bmpIsArrayDIB(...) sprawdza, czy tablica bBmpArray() zawiera dane odpowiadające nieskompresowanej bitmapie o 24-bitowej głębi kolorów i kierunku czytania bajtów obrazu „z dołu do góry”. Po pozytywnej weryfikacji sprawdza, czy dwa pierwsze bajty tablicy bBmpArray() są równe &H4D42 (odpowiadają sygnaturze 'BM'). Obecność sygnatury 'BM' odpowiada opcji „Zachowaj format obrazu bitowego”, co oznacza że tablica bBmpArray zawiera wszystkie bajty bitmapy (nagłówek BitmapFileHeader nagłówek BitmapInfoHeader oraz bajty obrazu). Brak sygnatury 'BM' odpowiada opcji „Konwertuj wszystkie dane obrazu na mapy bitowe” co oznacza, że tablica nie zawiera 14 bajtowego nagłówka BitmapFileHeader, który musi zostać odtworzony.
  • argumenty:
    • sDestFullPath()
    • pełna ścieżka dostępu wraz z nazwą pod jaką ma być zapisana bitmapa bitmapy
    • bBmpArray()()
    • tablica typu Byte (Image.PictureData) zawierająca bajty nieskompresowanej bitmapy
  • zwraca:
  • Przy powodzeniu zwraca TRUE a przy niepowodzeniu zwraca FALSE.
  • autor: Zbigniew Bratko
  • data: 27.02.2019
01. Czy tablica typy Byte jest prawidłową tablicą PictureData? Funkcja bmpIsArrayDIB(...)
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
Public Function bmpDibToDisc(bBmpArray() As Byte, sDestFullPath As String) As Boolean
Dim bfh           As BITMAPFILEHEADER
Dim ff            As Integer
Dim iAnswer       As Integer
 
  ' jeżeli plik istnieje, zapytaj użytkownika czy go nadpisać
  If plikFileExist(sDestFullPath) Then
    iAnswer = MsgBox("Plik docelowy" & vbNewLine & _
                    sDestFullPath & vbNewLine & _
                    "istnieje." & vbNewLine & _
                    "Czy chcesz go zastąpić?", _
                    vbExclamation + vbYesNo + vbDefaultButton2)
 
    If iAnswer = vbYes Then
      ' usuń plik
      Kill (sDestFullPath)
    Else
      Exit Function
    End If
  End If
 
  ' sprawdź, czy tablica zawiera poprawne dane 24-bitowej
  ' nieskompresowanej bitmapy bitmapy o kierunku odczytu bajtów
  ' „z dołu do góry”. Nie uwzględniaj przy sprawdzaniu bitmapy
  ' właściwości „Format przechowywania właściwości obrazów”
  If bmpIsArrayDIB(bBmpArray(), False) = False Then
    MsgBox "Tablica nie zawiera poprawnych danych bitmapy!"
    Exit Function
  End If
 
  ff = FreeFile
  ' otwórz plik w trybie Do zapisu
  Open sDestFullPath For Binary Access Write As #ff
    ' sprawdź, czy tablica zaczyna się sygnaturą 'BM'
    If Chr$(bBmpArray(0)) & Chr$(bBmpArray(1)) = cBmpSignatureBM Then
      ' tablica zawiera 14 bajtowy nagłówek BitmapFileFeader
      ' opcja: 'Zachowaj format obrazu źródłowego' MS Access 2007+
      Put #ff, , bBmpArray()
    Else
      With bfh
        ' odtwórz nagłówek BitmapFileHeader
        ' sygnatura 'BM' pliku bitmapy
        .bfType = &H4D42
        ' wielkość pliku bitmapy (DIB + 14)
        .bfSize = CLng(UBound(bBmpArray) + cBmpBfhSize + 1)
        ' 4 bajty rezerwowe
        .bfReserved1 = 0
        .bfReserved2 = 0
        ' przesunięcie do bitów bitmapy (14+40=54 bajty)
        .bfOffBits = cBmpBfhSize + cBmpBihSize
      End With
        'zapisz BitmapFileFeader
        Put #ff, , bfh
        ' zpisz pozostałe bajty
        Put #ff, , bBmpArray()
    End If
  ' zamknij plik
  Close #ff
 
  bmpDibToDisc = True
 
End Function

Słów kilka o stałych, zmiennych, strukturach i funkcjach.

Niby tak proste zadanie jak sprawdzenie 24-bitowej bitmapy, rozrosło się do dość pokaźnych rozmiarów. Mam zamiar kontynuować temat przetwarzania 24-bitowej bitmap w MS Access, więc jest to jedyna okazja bym uporządkował nieco funkcje i procedury operujące na bitmapach. Na początek utworzyłem kilka modułów standardowych:

  • bas_apiFun - deklaracje wszystkich funkcji API
  • bas_apiStruct - deklaracje wszystkich struktur API
  • bas_bmpFun - własne funkcje graficzne
  • bas_Const - deklaracje stałych
  • bas_Err - obsługa błędów
  • bas_plikiFun - własne funkcje dotyczące przetwarzania plików

i mam nadzieję, że w miarę przybywania przykładów i rozrastanie się modułów, jakoś utrzymam logiczny porządek we wszystkich utworzonych i tworzonych modułach

 

 
Akceptuję Polityka prywatności

Strona ta wykorzystuje pliki cookies w celu świadczenia usług, dostosowania serwisu do preferencji użytkowników oraz w celach statystycznych i reklamowych. Mechanizm zapisu plików cookies możesz wyłączyć w ustawieniach przeglądarki. Korzystanie z serwisu bez zmiany ustawień przeglądarki oznacza, że pliki cookies będą zapisywane na Państwa urządzeniu końcowym.