CImage::Load läd jpg-Datei anders als BMP-Datei



  • Ich versuche seit längerem eine jpg-Datei auf einem Ausdruck darzustellen. Dazu gab es bereits folgenden Beitag: Problem mit StretchBlt von einem CBitmap aus einem CImage
    Ich bin immer davon ausgegangen, dass StrechBlt das Problem verursacht. Dem ist aber nicht so. Es geht dabei um folgenden Code der in der View-Klasse beim Drucken aufgerufen wird:

    CDC memDC;
    CSize ptSize, ptOrg;
    memDC.CreateCompatibleDC(pDC);
    CBitmap* oldBitmap;
    CImage Image;
    Image.Load(ExpandGlobalAppDataPath(File_Firmenlogo_RGB));
    Image.Save(_T("D:\\NewImage.jpg"));		// habe ich testweise hinzugefügt
    CBitmap Firmenlogo;
    Firmenlogo.Attach(Image.Detach());
    oldBitmap = memDC.SelectObject(&Firmenlogo);
    
    BITMAP bi;
    Firmenlogo.GetBitmap(&bi);
    ptSize.cx = bi.bmWidth;
    ptSize.cy = bi.bmHeight;
    pDC->DPtoLP(&ptSize);
    
    // Skalieren des Firmenlogos auf cFirmenlogo_maxHeight
    dNewLogoWidth = dNewLogoHeight*static_cast<double>(ptSize.cx) / static_cast<double>(ptSize.cy);
    
    
    if (dNewLogoWidth > cFirmenlogo_maxWidth)	// falls durch die Skalierung der Höhe die Breite jetzt größer als cFirmenlogo_maxWidth geworden ist, muss das Logo wieder verkleinert werden
    {
    	dNewLogoHeight = dNewLogoHeight * (cFirmenlogo_maxWidth / dNewLogoWidth);
    	dNewLogoWidth = cFirmenlogo_maxWidth;
    }
    iLogoHeight = static_cast<int>(dNewLogoHeight);
    iLogoWidth = static_cast<int>(dNewLogoWidth);
    
    ptOrg.cx = 0;
    ptOrg.cy = 0;
    memDC.DPtoLP(&ptOrg); 		// macht hier keinen Sinn, jedoch wenn die Werte ungleich null sind
    m_rcPrintDataRect.top = m_rcPrintRect.top - iLogoHeight;
    m_rcPrintDataRect.bottom = m_rcPrintRect.bottom + iLogoHeight;
    
    pDC->StretchBlt(m_rcPrintRect.right - iLogoWidth, m_rcPrintRect.top, iLogoWidth, -iLogoHeight, &memDC, ptOrg.cx, ptOrg.cy, bi.bmWidth, bi.bmHeight, SRCCOPY);
    memDC.SelectObject(oldBitmap);
    

    Ich habe testweise Image.Save(_T("D:\NewImage.jpg")) eingeführt. Ist das zu ladende Bild eine BMP-Datei dann sieht die NewImage.jpg wie gewünscht aus und das Bild wird auch wie gewünscht gedruckt. Läd man eine identische jpg-Datei (gleiche Größe, gleiche Auflösung) dann ist die NewImage.jpg nur im linken oberen Viertel mit dem Originalbild gefüllt, der Rest ist schwarz. Wieso ist das so? Macht Image::Load dabei Unterschiede was das für ein Bild ist und wenn ja wie kann man das beeinflussen? Komme derzeit hier erst mal nicht weiter...


  • |  Mod

    @andydd sagte in CImage::Load läd jpg-Datei anders als BMP-Datei:

    CImage Image;
    Image.Load(ExpandGlobalAppDataPath(File_Firmenlogo_RGB));
    Image.Save(_T("D:\NewImage.jpg")); // habe ich testweise hinzugefügt

    Nur um es zu verstehen. Nur das Laden einer JPG und das Speichern unter einem neuen Namen als JPG führt zu dem Ergebnis?



  • Ja, direkt nach dem Laden und dem erneuten Speichern sieht die Datei anders als als vorher (schwarzer Hintergrund und das eigentliche Bild ist im linken oberen Viertel der neuen Datei verkleinert sichtbar).
    Was die Auflösung angeht, so muss ich mich da mal korrigieren. Habe gesehen das die Original-JPG-Datei eine Auflösung von 300 DPI x 300 DPI bei einer Bittiefe von 32 bit aufweist. Die BMP-Datei hingegen hat nur 96 DPI x 96 DPI bei 24 Bit Bittiefe. Kollidiert CImage mit irgendwas wenn man 32-Bit-Bilder verwendet?



  • Ich würd ja eher auf die DPI tippen.



  • @hustbaer Was meinst du da genau? Dass CImage keine 300 DPI verträgt? ich hab jetzt mal probiert alle in Frage kommenden Bilddateien zu testen- Egal wie groß die sind: sie funktionieren immer nur wenn die 24 bit eingestellt haben. Bei 32 bit kommt dieser Murks heraus. Leider finde ich nirgends was darüber das es irgendwelche Einschränkungen gibt bzw. wie man das CImage beibringen kann. Ist m_nBPP 32 dann hat der m_nPitch den Wert -7468, bei m_nBPP von 24 ist m_nPitch -5604. Kann man daraus was ableiten?



  • Bloss dass DPI gut zu "ist zu klein" passt, 32 Bit aber überhaupt nicht gut zu "ist zu klein" passt.

    Ist m_nBPP 32 dann hat der m_nPitch den Wert -7468, bei m_nBPP von 24 ist m_nPitch -5604. Kann man daraus was ableiten?

    Ja sicher: dass das Bild 1867 Pixel breit ist.



  • @hustbaer Ich fasse das noch mal zusammen. Ich verwende jetzt folgenden Code (in den Kommentaren stehen immer die aktuellen Werte der Größe des Bildes dahinter).

    CDC memDC;
    CSize ptSize, ptOrg;
    memDC.CreateCompatibleDC(pDC);
    		
    CImage Image;
    Image.Load(ExpandGlobalAppDataPath(File_Firmenlogo_RGB));
    ptSize.cx = Image.GetWidth();		// cx=1867
    ptSize.cy = Image.GetHeight();		// cy=360
    pDC->DPtoLP(&ptSize);			// nach diesem Aufruf ist cx=788 und cy=152
    		
    
    // Skalieren des Firmenlogos auf cFirmenlogo_maxHeight
    dNewLogoWidth = dNewLogoHeight*static_cast<double>(ptSize.cx) / static_cast<double>(ptSize.cy);
    
    if (dNewLogoWidth > cFirmenlogo_maxWidth)	// falls durch die Skalierung der Höhe die Breite jetzt größer als cFirmenlogo_maxWidth geworden ist, muss das Logo wieder verkleinert werden
    {
    	dNewLogoHeight = dNewLogoHeight * (cFirmenlogo_maxWidth / dNewLogoWidth);
    	dNewLogoWidth = cFirmenlogo_maxWidth;
    }
    iLogoHeight = static_cast<int>(dNewLogoHeight);		// =96
    iLogoWidth = static_cast<int>(dNewLogoWidth);		//=500
    // Skalierung soll so erfolgen, dass das Logo im Ausdruck nicht breiter als 500 (also 5 cm) oder nicht höher als 150 (also 1,5 cm) dargestellt wird. Letzteres ist noch nicht implementiert, da meist automatisch gegeben.
    ptOrg.cx = 0;
    ptOrg.cy = 0;
    memDC.DPtoLP(&ptOrg);
    m_rcPrintDataRect.top = m_rcPrintRect.top - iLogoHeight;
    m_rcPrintDataRect.bottom = m_rcPrintRect.bottom + iLogoHeight;
    
    // die Darstellung funktioniert nur korrekt, wenn die jpg-Datei eine Farbtiefe von 24 bit hat. 32 bit wird verkleinert und mit schwarzem Hintergrund dargestellt
    Image.StretchBlt(*pDC, m_rcPrintRect.right - iLogoWidth, m_rcPrintRect.top, iLogoWidth, -iLogoHeight, ptOrg.cx, ptOrg.cy, Image.GetWidth(), Image.GetHeight(), SRCCOPY);
    


  • @andydd sagte in CImage::Load läd jpg-Datei anders als BMP-Datei:

    CImage Image;
    Image.Load(ExpandGlobalAppDataPath(File_Firmenlogo_RGB));
    ptSize.cx = Image.GetWidth(); // cx=1867
    ptSize.cy = Image.GetHeight(); // cy=360

    Und was bekommst du da bei JPG zurück?



  • @andydd sagte in CImage::Load läd jpg-Datei anders als BMP-Datei:

    Ich fasse das noch mal zusammen. Ich verwende jetzt folgenden Code (in den Kommentaren stehen immer die aktuellen Werte der Größe des Bildes dahinter).

    Du verwendest ´DPtoLP´ falsch. Oder bist du sicher dass immer garantiert ist dass der die einzige Transformation zwischen Display-Koordinaten und logischen Koordinaten eine Skalierung ist? Wobei wenn das so wäre, dann wäre die ganze ptOrg Sache für die Würste und du könntest gleich 0, 0 verwenden.



  • @hustbaer sagte in CImage::Load läd jpg-Datei anders als BMP-Datei:

    @andydd sagte in CImage::Load läd jpg-Datei anders als BMP-Datei:

    CImage Image;
    Image.Load(ExpandGlobalAppDataPath(File_Firmenlogo_RGB));
    ptSize.cx = Image.GetWidth(); // cx=1867
    ptSize.cy = Image.GetHeight(); // cy=360

    Und was bekommst du da bei JPG zurück?

    Bei JPG wird das Gleiche zurückgeliefert. Der Variablenname ist irreführend gewählt. Da ich den noch an anderen Stellen verwende habe ich den noch nicht geändert. Beim Initialisieren bekommt der den Dateipfad zugewiesen.

    @hustbaer sagte in CImage::Load läd jpg-Datei anders als BMP-Datei:

    @andydd sagte in CImage::Load läd jpg-Datei anders als BMP-Datei:

    Ich fasse das noch mal zusammen. Ich verwende jetzt folgenden Code (in den Kommentaren stehen immer die aktuellen Werte der Größe des Bildes dahinter).

    Du verwendest ´DPtoLP´ falsch. Oder bist du sicher dass immer garantiert ist dass der die einzige Transformation zwischen Display-Koordinaten und logischen Koordinaten eine Skalierung ist? Wobei wenn das so wäre, dann wäre die ganze ptOrg Sache für die Würste und du könntest gleich 0, 0 verwenden.

    Das kann gut sein (das ich es falsch verwende oder das noch sonst wo durch das Framework eine Umrechnung ausgeführt wird), deshalb frage ich ja. Ich selbst verwende zur Darstelung nur diesen Code. Aufgerufen wird er in der OnPrint der Viewklasse, da sich die Druckdarstellung von der grafischen Oberfläche (behandelt in OnDraw) deutlich unterscheidet.
    Ich hatte oben erwähnt, dass aktuell ptOrg 0,0 ist und damit wie du schon sagst die Transformation sinnlos ist. Wenn man den aber verschieben würde muss man transformieren. Meinst Du das der erneute Aufruf das Problem generiert? Umrechnen muss ich, weil ich ja MM_LOMETRIC verwende.



  • Also grundsätzlich ist es natürlich möglich dass CImage nen Bug hat und nicht mit 32BPP Bildern funktioniert. Hast du dazu schon mal gegoogelt? Oder in den Source reingesehen?



  • @hustbaer sagte in CImage::Load läd jpg-Datei anders als BMP-Datei:

    Also grundsätzlich ist es natürlich möglich dass CImage nen Bug hat und nicht mit 32BPP Bildern funktioniert. Hast du dazu schon mal gegoogelt? Oder in den Source reingesehen?

    Ja hab ich. Bereits hier im Forum wurden ähnliche Dinge beschrieben, wo es dann trotzdem keine endgültige Lösung gab. Es gab auch Unterscheide zwischen der Darstellung in der Druckvorschau und dem Papierausdruck. Die Load-Funktion von CImage verwendet intern CreateFromGdiplusBitmap(). Wenn ich den Quelltext richtig interpretiere, dann gehen die zunächst immer erst mal von 32 bit aus und wenn das Bild weniger hat wird es angepasst. Aber ich bin da auch nicht der Experte.



  • Hallo,

    lade deine Bilder jpg,bmp,gif,png.. über ole:

     ... Code ... 
     // Read file in memory
       fp = fopen(fileName,"rb");
       if(!fp) 
    	return;
      
       BYTE *pMem  = new BYTE[fs];
       if(!pMem)
        return ;
    
       fread(pMem,1,fs,fp);
       fclose(fp);
    
       if(!StreamToPic(pMem,fs))
       {
        delete pMem;
        return ;
       }
    
       delete pMem;
       
       
    
    bool CDib::StreamToPic(BYTE *pMem, int Size, bool Flip/*=false*/)
    {
       // Use IPicture stuff to use JPG / GIF files
       IPicture* pPicture = 0;
       IStream* pStream   = 0;
       LPBYTE   hG        = 0;
       HRESULT  hRes      = S_FALSE;
      
       DWORD fs = Size;
       hG = (LPBYTE) HeapAlloc( GetProcessHeap(), HEAP_GENERATE_EXCEPTIONS, fs);
       if(!hG)
        return hRes;
     
       memcpy(hG,pMem,fs);
       
       HRESULT hRes = ::CreateStreamOnHGlobal(hG,false,&pStream);// Create an IStream so IPicture can 
       if(hRes != S_OK)
       {
        HeapFree(GetProcessHeap(), 0, hG);
        return hRes;
       }
    
       hRes = ::OleLoadPicture(pStream,fs,TRUE,IID_IPicture,(void**)&pPicture);
      
       if(hRes != S_OK)
       {
          pStream->Release();
          HeapFree(GetProcessHeap(), 0, hG);
          return hRes;
       }
    
       short type;pPicture->get_Type(&type);
       if(type == PICTYPE_BITMAP)
       {
    		HBITMAP hBmp = 0;
    		hRes = pPicture->get_Handle( (OLE_HANDLE* )&hBmp);//Retrive GDI Object 
    
    		if (hRes == S_OK)
    		{
    			CDib::Draw(hBmp, Flip);
    		}
        
    		if(hBmp)::DeleteObject(hBmp);
       }
    	
       if(pStream)
        pStream->Release();
    
       if(hG)
    	  HeapFree(GetProcessHeap(), 0, hG);
    
       if(pPicture)
        pPicture->Release();
    
       return (hRes == S_OK);
    }