C++ Text zu Bitmap
lima-city → Forum → Programmiersprachen → C/C++ und D
alpha
anschauen
array
bild
code
frage
http
idee
image
klasse url
muster
null
problem
string
text
url
verwenden
windows
zeichen
zeichnen
-
Hallo Leute,
ich bin ja noch relativ neu in C++, deshalb häufen sich so die Probleme. Eines davon konnte ich noch nicht lösen:
Ich habe ein Bitmap Array, welches so aufgebaut ist:
b g r a b g r a b g r a b g r a... 0 1 2 3 4 5 6 7 8 9 101112131415...
Dabei steht
b=blau
g=grün
r=rot
a=alpha
Das ganze ergibt dann eine Rastergrafik.
Nun das Problem: Ich habe einen String. Wie konvertiere ich diesen in eine Rastergrafik?
OS ist Windows, das heißt ich hab Zugang zu GDI+ (Das war meine erste Idee, hat aber nicht funktioniert)
Grüße
Mator -
Diskutiere mit und stelle Fragen: Jetzt kostenlos anmelden!
lima-city: Gratis werbefreier Webspace für deine eigene Homepage
-
Die Idee hatte ich auch schon, allerdings möchte ich die einzelnen Buchstaben in unterschiedlichen Größen und auch dick haben. Und das wäre dann doch etwas viel Arbeit...
-
Hallo mator-kaleen,
mit GDI bzw. GDI+ liegst Du eigentlich richtig. Du musst Dein Bild (Bitmap-Array) nur mit Hilfe von CreateDIBSection() erzeugen.
#include <Windows.h> #include <iostream> #include <fstream> class BMPImage { private: int width; int height; unsigned char ** data; HBITMAP hbm; public: BMPImage(int width, int height) { int i, line_size; BITMAPINFO bih = {0}; HDC hdc; if(width<1 || height<1) { throw new std::invalid_argument("Invalid image size."); } this->width = width; this->height = height; try { this->data = new unsigned char*[height]; } catch(std::bad_alloc&) { throw new std::bad_alloc("Allocation for image rows failed."); } bih.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); bih.bmiHeader.biWidth = width; bih.bmiHeader.biHeight = height; bih.bmiHeader.biPlanes = 1; bih.bmiHeader.biBitCount = 24; bih.bmiHeader.biCompression = BI_RGB; hdc = CreateCompatibleDC(0); this->hbm = CreateDIBSection(hdc,&bih,DIB_RGB_COLORS,(void**)&this->data[0],0,0); DeleteDC(hdc); if(!this->hbm) { delete[] this->data; throw new std::bad_alloc("Allocation of DIB section failed."); } if((width)%4) line_size = 3 * width + (4 - (width * 3) % 4); else line_size = 3 * width; for(i=1;i<height;i++) this->data[i]= &this->data[0][line_size*i]; } ~BMPImage() { DeleteObject(this->hbm); delete[] this->data; } int getWidth() { return this->width;} int getHeight() { return this->height;} HBITMAP getHandle() { return this->hbm; } COLORREF getPixel(int x, int y) { if(x < 0 || x >= this->width) { throw new std::invalid_argument("x value out of bounds."); } if(y < 0 || y >= this->height) { throw new std::invalid_argument("y value out of bounds."); } return RGB(this->data[y][3*x+2],this->data[y][3*x+1],this->data[y][3*x]); } void setPixel(int x, int y, COLORREF color) { if(x < 0 || x >= this->width) { throw new std::invalid_argument("x value out of bounds."); } if(y < 0 || y >= this->height) { throw new std::invalid_argument("y value out of bounds."); } this->data[y][3*x] = GetBValue(color); this->data[y][3*x+1] = GetGValue(color); this->data[y][3*x+2] = GetRValue(color); } bool save(const char * filename) { std::ofstream * fout; BITMAPFILEHEADER bfh = {0}; BITMAPINFOHEADER bih = {0}; RGBQUAD rgbq = {0}; int line_size; if(!filename) { throw new std::invalid_argument("filename is null."); } if((this->width)%4) line_size = 3* this->width + (4 - (this->width * 3) % 4); else line_size = 3 * this->width; bfh.bfType = MAKEWORD('B', 'M'); bfh.bfSize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD) + this->height * line_size; bfh.bfReserved1=0; bfh.bfReserved2=0; bfh.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD); bih.biSize = sizeof(BITMAPINFOHEADER); bih.biWidth = this->width; bih.biHeight = this->height; bih.biPlanes = 1; bih.biBitCount = 24; bih.biCompression = BI_RGB; fout = new std::ofstream(filename, std::ios::out|std::ios::binary); if(fout->fail()) { delete fout; return false; } fout->write((const char *)&bfh,sizeof(BITMAPFILEHEADER)); fout->write((const char *)&bih,sizeof(BITMAPINFOHEADER)); fout->write((const char *)&rgbq, sizeof(RGBQUAD)); fout->write((const char *)this->data[0],line_size*this->height); if(fout->fail() || fout->bad()) { fout->close(); return false; } else { fout->close(); return true; } } }; HFONT getFont(const char * name, int size, bool italic = false, bool bold = false, bool underline = false, bool strike_out = false) { LOGFONT lfont = {0}; lfont.lfQuality = CLEARTYPE_QUALITY; lfont.lfCharSet = DEFAULT_CHARSET; strncpy(lfont.lfFaceName, name, 31); lfont.lfHeight = -size; if(italic) lfont.lfItalic = TRUE; if(bold) lfont.lfWeight= FW_BOLD; if(underline) lfont.lfUnderline = TRUE; if(strike_out) lfont.lfStrikeOut = TRUE; return CreateFontIndirect(&lfont); } int main(int argc, char ** argv) { // create image BMPImage * img = new BMPImage(256, 256); // create device context for painting HDC hdc = CreateCompatibleDC(NULL); // select bitmap into DC HBITMAP hbm_old = (HBITMAP)SelectObject(hdc, img->getHandle()); // define rect for painting RECT rect = {0,0,img->getWidth(), img->getHeight()}; // draw white background FillRect(hdc, &rect, WHITE_BRUSH); // create and select font HFONT font = getFont("Comic Sans MS", 40, false, true, true); HFONT font_old = (HFONT)SelectObject(hdc, font); // set text color and background mode SetTextColor(hdc, RGB(255, 0, 128)); SetBkMode(hdc, TRANSPARENT); // draw centered text DrawText(hdc, "Hallo", -1, &rect, DT_SINGLELINE|DT_CENTER|DT_VCENTER); // replace DC bitmap and font by original one SelectObject(hdc, font_old); SelectObject(hdc, hbm_old); // free font and device context DeleteObject(font); DeleteDC(hdc); // save image img->save("test.bmp"); // clean up delete img; return 0; }
Alpha-Kanal ist in dem Beispiel zwar nicht dabei. Aber zumindest kannst Du Dir hier anschauen, wie man eine Bitmap erzeugt, diese in einen Device Context lädt, Text reinmalt und am Ende alles als .bmp speichert.
Beitrag zuletzt geändert: 13.3.2013 22:52:42 von darkpandemic -
Vielen Dank für die Antwort! Ich glaub, dass mich das sehr viel weiter bringt.
Eine Frage hätte ich noch: Was wäre, wenn ich Linux verwenden würde? Müsste ich dann auf OpenGL umsteigen oder wie könnte ich es da machen?
Ich will jetzt keinen Beispielcode, ich will nur das Stichwort wissen (da mein Programm eh nie auf Linux zum Einsatz kommen wird), einfach nur der Interesse wegen :)
Lg
Mator -
Hallo mator-kaleen,
erstmal eine kleine Korrektur:
Das erzeugte Bild liegt im Bottom-Up- und nicht im Top-Down-Fromat vor. D.h. die erste Datenzeile entspricht der untersten Bildzeile und nicht der obersten. Daher müssen die getPixel()- und setPixel()-Methoden leicht modifiziert werden um das übliche Verhalten zu bekommen:
COLORREF getPixel(int x, int y) { if(x < 0 || x >= this->width) { throw new std::invalid_argument("x value out of bounds."); } if(y < 0 || y >= this->height) { throw new std::invalid_argument("y value out of bounds."); } // invert y coordinate y = this->height-1-y; return RGB(this->data[y][3*x+2],this->data[y][3*x+1],this->data[y][3*x]); } void setPixel(int x, int y, COLORREF color) { if(x < 0 || x >= this->width) { throw new std::invalid_argument("x value out of bounds."); } if(y < 0 || y >= this->height) { throw new std::invalid_argument("y value out of bounds."); } // invert y coordinate y = this->height-1-y; this->data[y][3*x] = GetBValue(color); this->data[y][3*x+1] = GetGValue(color); this->data[y][3*x+2] = GetRValue(color); }
Mit OpenGL wirst Du nicht weiter kommen, da OpenGL von sich aus kein Font-Rendering unterstützt. Um Text mit OpenGL anzuzeigen muss man auch erst den Text in eine Bitmap bzw. Textur zeichnen und diese dann in die Graphikkarte übertragen.
Allerdings kannst Du obigen Code natürlich für diese Vorstufe verwenden.
Wenn man unter Linux Text rendern will, dann kann man es einerseits auf die harte Tour machen indem man die FreeType Library verwendet. Das ist der absolute Low-Level Ansatz und sich das anzutun ist wohl sehr lehrreich aber in der Praxis wird man es eher nicht damit machen.
In der Dokumentation dazu sind auch Tutorials bei denen man sich anschauen kann, wie so etwas aussieht.
Unter Linux ist es vernünftiger die Graphikfunktionen eines Toolkits zu verwenden. Für C++ kann man da z.B. QT oder wxWidgets verwenden. Dabei hat man sogar den Vorteil, dass es nicht nur unter Linux sondern auch unter Windows läuft.
Unter QT wäre die QPainter-Klasse das Analogon zum Device Context und QImage kann man als Bild verwenden. Bei wxWidgets gibt es die wxDC-Klasse als normalen Device Context und die wxMemoryDC-Klasse um in Bilder (wxBitmap) zu zeichnen.
Ich persönlich fand wxWidgets einfacher zu handhaben weshalb ich es jetzt einfach mal für den Anfang empfehle.
In QT sind mittlerweile schon so viele andere Techniken drin, dass man sich als Anfänger unter Umständen nur schwer zurechtfindet. -
Diskutiere mit und stelle Fragen: Jetzt kostenlos anmelden!
lima-city: Gratis werbefreier Webspace für deine eigene Homepage