kostenloser Webspace werbefrei: lima-city


C++ Text zu Bitmap

lima-cityForumProgrammiersprachenC/C++ und D

  1. Autor dieses Themas

    m**********n

    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
  2. Diskutiere mit und stelle Fragen: Jetzt kostenlos anmelden!

    lima-city: Gratis werbefreier Webspace für deine eigene Homepage

  3. Es geht sicher einfacher, aber ich würde Muster definieren für jedes in Frage kommende Zeichen und dann den String zeichenweise konvertieren nach dem Muster:

    Welches Zeichen ?
    Füge zugehöriges Bitmuster zum Array hinzu.
    Weiter mit dem nächsten Zeichen.
  4. Autor dieses Themas

    m**********n

    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...
  5. 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
  6. Autor dieses Themas

    m**********n

    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
  7. 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.
  8. Diskutiere mit und stelle Fragen: Jetzt kostenlos anmelden!

    lima-city: Gratis werbefreier Webspace für deine eigene Homepage

Dir gefällt dieses Thema?

Über lima-city

Login zum Webhosting ohne Werbung!