Wydawać się może to dość dziwne, że informacje na temat klasy QImage służącej do obsługi bitmap opisuję w dziale dotyczącym programowania konsolowego. Podjąłem jednak taką decyzję, gdyż w ten sposób mogę pokazać podstawy operacji możliwych do wykonania na bitmapach bez konieczności posiadania GUI.
Konfiguracja projektu
Ponieważ klasa QImage domyślnie jest przeznaczona dla projektów GUI, więc aby dostęp do pliku nagłówkowego QImage był możliwy konieczna jest edycja pliku z rozszerzeniem .pro, który jest dostępny w każdym projekcie w oknie pokazanym na poniższym rysunku.
Po dwukrotnym kliknięciu (w moim przypadku pozycji ImageExample.pro) oczom twym powinien ukazać się następujący kod:
Klasa QImage umożliwia odczyt/zapis plików następujących formatów:
Odczyt bitmapy z pliku i podstawowe jego parametry
Jak już wspomniałem klasa QImage umożliwia odczyt różnych formatów plików zawierających obraz rastrowy. Taką możliwość wczytania danych daje sam konstruktor klasy QImage:
W powyższym kodzie odczytany został plik fog.jpg oraz wyświetlona została informacja o jego rozmiarze i głębi (liczbie bitów na piksel). Niestety najwyraźniej klasa QImage zamienia 24 bitową bitmapę automatycznie przy wczytywaniu na 32 bitową (czyli z kanałem alfa).
Po wczytaniu bitmapy można pozyskać informację o kolorze danego jej piksela. Do tego celu służy metoda pixel, oto przykład jej użycia:
int x = 100;
int y = 100;
QRgb color = image.pixel(x, y);
qDebug() << "Kolor z pozycji" << x << ";" << y << ":" << QString("czerwony = %1; zielony = %2; niebieski = %3; alfa = %4").arg(qRed(color)).arg(qGreen(color)).arg(qBlue(color)).arg(qAlpha(color));
Wynik działania powyższego kodu:
Kolor z pozycji 100 ; 100 : "czerwony = 198; zielony = 178; niebieski = 145; alfa = 255"
Możliwe jest również ustawienie koloru danego piksela bitmapy za pomocą metody setPixel:
int x = 100;
int y = 100;
QRgb color = image.pixel(x, y);
qDebug() << "Kolor z pozycji" << x << ";" << y << ":" << QString("czerwony = %1; zielony = %2; niebieski = %3; alfa = %4").arg(qRed(color)).arg(qGreen(color)).arg(qBlue(color)).arg(qAlpha(color));
image.setPixel(x, y, qRgba(0,0,0,255));
color = image.pixel(x, y);
qDebug() << "Kolor z pozycji" << x << ";" << y << ":" << QString("czerwony = %1; zielony = %2; niebieski = %3; alfa = %4").arg(qRed(color)).arg(qGreen(color)).arg(qBlue(color)).arg(qAlpha(color));
Wynik działania powyższego kodu:
Kolor z pozycji 100 ; 100 : "czerwony = 198; zielony = 178; niebieski = 145; alfa = 255"
Kolor z pozycji 100 ; 100 : "czerwony = 0; zielony = 0; niebieski = 0; alfa = 255"
Operacje na pikselach za pomocą powyższych metod niestety są bardzo powolne, dlatego można też operować na poszczególnych pikselach bitmapy wykorzystując metodę bits, która zwraca wskaźnik do tablicy typu uchar (unsigned char) reprezentującej składowe kolorów pikseli bitmapy w układzie: czerwony, zielony, niebieski, alfa. Oto przykład wykorzystania tego wskaźnika do rozjaśnienia kolorów bitmapy:
#include <QCoreApplication>
#include <QDebug>
#include <QImage>
void minmax(uchar* value, int add){ // normalizacja wartości by nie przekraczała zakresu 0 - 255
*value = (int)*value + add < 255 ? ((int)*value + add > 0 ? *value + add : 0) : 255;
}
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
QImage image("fog.jpg", "jpg");
uchar* bits = image.bits(); // pozyskuję wskaźnik na początek tablicy bitów
uchar* bitsEnd = bits + image.byteCount(); // obliczam wskaźnik na koniec tablicy
int add = 50; // dodawana wartość
for(uchar* index = bits; index < bitsEnd; index += 4){
minmax(index, add); // składowa czerwona
minmax(index + 1, add); // składowa zielona
minmax(index + 2, add); // składowa niebieska
}
image.save("fog2.jpg");
return a.exec();
}
Wynik działania powyższego kodu na przykładowej bitmapie pokazany został na poniższym rysunku.
Zapis obrazu do pliku
Obraz można również zapisać do pliku za pomocą metody save, która w przypadku niektórych formatów umożliwia również ustawienie poziomu kompresji:
image.save("fog2.jpg"); // z domyślną kompresją
image.save("fog3.jpg", "jpg", 0); // z najniższą możliwą kompresją
image.save("fog4.jpg", "jpg", 100); // z największą możliwą kompresją