Problem unsigned char auf LPCSTR?



  • Hallo zusammen,

    ich schreibe gerade ein Tool indem ich ein PNG File einlesen und ein 32Bit Array wandeln kann um es als Textur einbinden zu können im Projekt meiner Diplomarbeit... Folgendes Problem:

    Ich habe die Bilddaten in einem unsigned Byte Array über die DevIL Library

    ILubyte * bytes = ilGetData() ;
    

    das das array unsigned is kann ich meines Erachtens nich ändern ohne im Headerfile rumzupfuschn...
    Wichtig: Für die DevIL Lib ist bei mir der UNICODE deaktiviert! Deswegen hab ich natürlich jetzt wenn ich das in ein file exportier ANSI Code *jaigitt* 😕
    Ich hab nun das Array in einen String gepackt um diesen mit ::MultiByteToWideChar() in UNICODE zu wandeln und dann per ofstream in ein File zu packen:

    //Create Stream for Output and Open File
      ofstream fp_out;
      fp_out.open("pngdata.txt", ios::out);
    
      //Convert Array to string
      unsigned char *str;
      str = new unsigned char[size+1];
      for(int i = 0; i < size; i++){
    	  str[i] = bytes[i];
      }
    
        //Change Array to Unicode
    
    	BSTR unicodestr = SysAllocStringLen(NULL, size);
    	::MultiByteToWideChar(CP_ACP, 0, str, size, unicodestr, size);
    

    Wenn ich es so mache krieg ich nen Error, da str unsigned char ist aber vom der Funktion MultiByteToWideChar() als LPCSTR erwartet wird 😕
    Wenn ich aber mein Byte Array auf einen signed string caste konvertiert er zwar aber ich bekommen falsche Werte. Das liegt vermutlich daran, dass ich beim casten von unsigned auf signed negative Werte bekomme, die dann natürlich über ANSI Tabelle konvertiert quark mit Soße sind 🤡

    Zum Verständnis allgemein: Im Byte Array bytes[i] befinden sich RGBA Werte des PNG also zB 0,0,0,255. Was ich am Ende im Textfile haben will ist folgende Darstellung: 0x000000ff.

    Ich hoff es hat vielleicht einer nen Tipp wie ich das Problem lösen kann, da häng ich grad ziemlich an der Wand 😞

    Grüße
    Max :xmas1:



  • Also ich verstehe im Moment nicht so ganz, warum du den Umweg über die Textdatei gehst.
    DevIL ist doch dafür gedacht, das png direkt zu laden und kann das auch gleich an OpenGL weitergeben.



  • Hi Decimad;

    das is natürlich eine berechtigte Frage. Dazu muss ich kurz ausholen.
    Ich bin leider auch neu in OpenGL und programmier ein einfaches Instrument Cluster. Dabei hab ich ein Example vorliegen welches die Texturdaten per Header mit einem 32BIT Array einpflegt. Um mir den Einstieg erstmal zu erleichtern wollte ich die selbe Struktur wie im Example beibehalten...



  • Okay, wenn es die Sache einfacher macht, warum nicht...
    Ich würde das Array zu einem unsigned int array casten, den Stream auf hexadezimal einstellen und dann einfach die unsigned int's drauf loslassen. Ich hoffe, ich habe das jetzt richtig interpretiert, dass du das als echte Strings in der Textdatei haben möchtest.

    Edit: Also ich komme nicht umhin zu denken, dass du da mit Strings im allgemeinen etwas nicht verstanden hast. Was OpenIL da zurückliefert ist ein Array von Byte-Werten die die entsprechende Farbkompenente spezifizieren für Pixel. Was MultibyteToWideChar macht ist Multibyte (oder Ansi/Ascii/Utf8)-Kodierte Strings in UTF16-Strings zu rekodieren. Dafür muss man aber überhaupt erstmal einen gültigen String der Form haben. "0x000000FF" hat halt als String 10 Byte, während das was OpenIL da liefert wirklich binär ist, also pro Pixel 4 Byte. Eine Ascii-0 ist eben auch keine Binär-0, sondern im Speicher eine 48. Du musst daher, wie ich vorschlage, die Zahlen entsprechen in ihre Stringform bringen, was die entsprechenden Ausgabeoperatoren vom Stream erledegen.



  • ok dann mach ich mal den crosscheck 🙂

    Decimad schrieb:

    OpenIL da zurückliefert ist ein Array von Byte-Werten die die entsprechende Farbkompenente spezifizieren für Pixel.

    check. versteh ich. ich hab im Array quasi 0,0,0,255 also RGBA. Gleich mal vorweg das ist in ANSI da UNICODE ja deaktiviert ist.

    Decimad schrieb:

    Was MultibyteToWideChar macht ist Multibyte (oder Ansi/Ascii/Utf8)-Kodierte Strings in UTF16-Strings zu rekodieren.

    check. versteh ich. die benutze ich ja genau aus dem Grund. damit will ich aus den ANSI Daten Unicode machen, da krieg ich ja sonst symbolvielfalt wenn ich das ohne konvertierung exportier...

    Decimad schrieb:

    "0x000000FF" hat halt als String 10 Byte, während das was OpenIL da liefert wirklich binär ist, also pro Pixel 4 Byte. Eine Ascii-0 ist eben auch keine Binär-0, sondern im Speicher eine 48. Du musst daher, wie ich vorschlage, die Zahlen entsprechen in ihre Stringform bringen, was die entsprechenden Ausgabeoperatoren vom Stream erledegen.

    Jetz wirds interessant 🙂 daran hab ich so genau noch gar nich gedacht. 4 BytepP sind richtig, aber wenn ich 0,0,0,255 in hex wandle hab ich doch 000000FF? Ich dachte mein Problem is das meine 0-255 rgba werte durch den pflicht-signed-cast für die Funktion MyByte zu -127...+128 wird und dann die negativWerte falsch bei der ANSI konvertierung interpretiert werden...

    Decimad schrieb:

    Ich würde das Array zu einem unsigned int array casten .

    der ist schon unsigned char von der IL.h aus.
    den Streamc aste ich schon hex ins file, dass mache ich so während der filestream offen ist.:

    for(j = 0; j < size; j++){
    
    	  if(j%4==0){
    		fp_out << "0x";
    		//printf("0x");
    		}
    
    	  fp_out << setw(2) << setfill('0') << hex << unicodestr[j];
    	  //fp_out << setprecision(2) << hex << unicodestr[j];
    
    	  //New Line
    	  if((j+1)%4==0){
    	  fp_out << ";\n";
    	  //printf(";\n");
    	  }
      }
    

    das macht auch was es soll...

    Danke für die schnelle Antwort!
    Gruß Max



  • OpenIL gibt kein Ascii oder Ansi oder überhaupt eine Text-Kodierung zurück. OpenIL gibt binärepresentation der Farbwerte zurück, wie zahlen halt im PC intern dargestellt werden (Denke daran dass bspw. 254 in ein Byte passt, aber 3 Schriftzeichen in Textkodierung braucht, also in Ascii 3 Byte oder UTF-16 halt 6 Byte). Deshalb müssen die Zahlen erst zu Ascii, Ansii oder UTF konvertiert werden.

    Was ich mit den unsigned int-Array meinte war:

    const unsigned int* intPtr = reinterpret_cast<const unsigned int*>( byteArray );
    
    for( unsigned int i=0; i<numPixels; ++i )
    {
        stream << intPtr[i];  // Hier wird unsigned int zu einem String konvertiert
    }
    


  • so da bin ich wieder. Also erstmal, es ist definitiv eine funktionierende Lösung, ganz hingekriegt hab ichs aber doch ned 😞 hab aber erstmal versucht es selbst zu lösen ohne Erfolg...

    //Create Stream for Output and Open File
      ofstream fp_out;
      fp_out.open("pngdata.txt", ios::out);
    
      //typecast byte array
        const int* intPtr = reinterpret_cast<const int*>( bytes ); 
    	for( int i=0; i<size; ++i ) 
    	{ 
    		fp_out << hex << ";" <<intPtr[i];  // Hier wird unsigned int zu einem String konvertiert 
    		cout << hex << "\n" << intPtr[i];
    	}
    

    gibt mir korrekt im File aus : FF000000;FF000000; etc und dachte das da halt ein Dreher drin ist weil ja eigentlich RGBA und das schaut nach ABGR aus aber dazu später ... Ich bekommen aber gegen Ende dann immer nen Zugriffsfehler beim Lesen. Das passiert ca bei einem i=300000, also liegst nich am i hät ich gesagt.
    Den Fehler krieg ich ja normal wenn ich was mit den arrayelementen falsch gemacht hab.Also hab ich mir das Array genauer angeschaut:
    Wenn ich intPtr decimal ausgeben lass krieg ich zB -16700000 oder ähnlich hohe Zahlen im negativen Bereich. Meineserachtens doch falsch oder? Dann hab ich bisl rumgespielt mit nem unsigned typecast etc aber ohne Erfolg.

    Dann ist mir augefallen das bytes[] vom Typ char ist

    ILubyte * bytes = ilGetData();
    

    mit typedef unsigned char ILubyte
    dh meine Werte von OpenIL sind gar keine "zahlenwerte" sonder chars? Wenn ich dann nen cast mache auf ein integer array kann das doch nich gutgehn oder?

    Debenbei bemerkt:
    Jetz hab ich auch glaub ich besser verstanden was du meintest. Wenn mir OpenIL die Daten in Zahlenwerten liefert, dann wär mir ANSI ansich wurscht wenn ichs einfach als hex in den Stream schiebe. Sobald ich irgendwas mit chars mache kommt ANSI ins spiel, right?

    Gruß Max



  • Sanschoo schrieb:

    gibt mir korrekt im File aus : FF000000;FF000000; etc und dachte das da halt ein Dreher drin ist weil ja eigentlich RGBA und das schaut nach ABGR aus

    http://de.wikipedia.org/wiki/Byte-Reihenfolge

    Sanschoo schrieb:

    Ich bekommen aber gegen Ende dann immer nen Zugriffsfehler beim Lesen. Das passiert ca bei einem i=300000, also liegst nich am i hät ich gesagt.

    Ist size die Länge des unsigned char-Arrays? Die musst du natürlich durch vier teilen, wenn du das als Array von 32-Bit-Werten interpretierst.

    Sanschoo schrieb:

    Dann ist mir augefallen das bytes[] vom Typ char ist

    ILubyte * bytes = ilGetData();
    

    mit typedef unsigned char ILubyte
    dh meine Werte von OpenIL sind gar keine "zahlenwerte" sonder chars?

    char ist ein Zahl-Datentyp.

    Sanschoo schrieb:

    Wenn ich dann nen cast mache auf ein integer array kann das doch nich gutgehn oder?

    Doch, das ist ja der Sinn der ganzen Aktion. Du willst ja gerade den Inhalt des Speichers uminterpretieren.



  • Klasse! Das war der Clou! Warum durch vier teilen is mir nun auch klar 🙂 somit kriege ich mit folgendem Code meinen formal gewollten Output:

    //typecast byte array
    
    	const int *intPtr = reinterpret_cast<const int*>( bytes ); 
    	for( int i=0; i<(size/4); i++ ) 
    	{ 
    		fp_out << hex << showbase << ", " << intPtr[i];  // Hier wird unsigned int zu einem String konvertiert 
    		//cout << hex << "\n" << intPtr[i];
    	}
    

    Ein Problem hab ich jetz noch (und wieder hab ichs versucht -> openGL noob 🙄 )... Format jetz 0xFF000000. Ich bin mir ziemlich sicher das das ABGR ist, da ich ein Schwarz weiss bild geladen habe und der AlphaWert auf 255 sein müsste. Jetzt dachte ich mir ich lads mal in mein OpenGL Programm

    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1024, 512, 0, GL_RGBA, GL_UNSIGNED_BYTE, pointer_zum_32bit_array);
    

    Mein PNG ist mit paint auf 1024*512 geändert mit einer 32bit-Farbtiefe.

    Dann krieg ich nen Fehler mit Zugriffsverletzung 😮 versteh ich nich. Selbst wenns RGBA is, müsste ers doch trotzdem nehmen und halt dann falsche Farben anzeigen?!
    Wenn ich GL_RGB ohne alphawert nehme macht ers, zeigt aber auch falsche Farben an (weil vermutlich BGR)... sorry wenn ich da so aufm Schlauch stehe 🙄

    Und wieso drehts mir denn überhaupt meinen arrayinhalt?! Wenn ich mein bytes[] mit printf ausgebe hab ich 0,0,0,255 (also richtig). Und mit der for-Schleife schreibe ich sie nach dem reinterpret-cast der Reihe nach ins file...

    Gruß Max



  • Sanschoo schrieb:

    Mein PNG ist mit paint auf 1024*512 geändert mit einer 32bit-Farbtiefe.

    Wie groß ist denn size?

    Sanschoo schrieb:

    Und wieso drehts mir denn überhaupt meinen arrayinhalt?!

    Hast du den Wikipedia-Artikel gelesen?



  • size = 2 097 152

    ja jetzt gleich nochmal gelesen. Mit Windows etc befinde ich mich ja auf little endian und hab nun offensichtlich big endian vorliegen. Aber warum das beim auslesen eines PNGs passiert konnte ich dem Text nicht entnehmen. Oder ist es sorum, dass RGBA eigentlich big endian ist, und der ichs dann umgekehrt interpretiert bekommen weil Windows little endian?!



  • edit: alles klar stimmt, PNG ist big endian

    http://www.w3.org/TR/2003/REC-PNG-20031110/#7Integers-and-byte-order

    damit ists erklärt woher der Dreher kommt...

    Bleibt also noch warum mir OpenGL bei 32bit format per GL_RGBA nichts einlesen möchte... 🙂



  • Vielleicht fragst du da besser im Grafik-Forum.



  • Passt, ich habs hinbekommen indem ichs mit einer ByteSwap Function noch gedreht hab...

    Wollte noch ein dickes Danke an euch, Decimad und MFK, sagen. Ihr wart damit wirklich eine große Hilfe bei meiner Diplomarbeit. Habe damit das Problem nicht nur lösen sondern auch wirklich verstehen können...
    Werd das Forum öfters besuchen um auchmal was beitragen zu können!

    Grüße Max


Anmelden zum Antworten