| Autor |
Nachricht |
nwp3
Unregistrierter
|
nwp3 Unregistrierter
23:53:40 26.06.2012 Titel: |
Effizient Bilddaten aus Resource laden |
Zitieren |
Ich habe ein Bitmap als Resource und benötige die Rohdaten um diese in eine OpenGL-Textur zu kopieren. Im Moment mache ich folgendes:
| C++: | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 | HBITMAP hbmp = (HBITMAP)LoadImage(GetModuleHandle(0), MAKEINTRESOURCE(MY_BITMAP_RESOURCE), IMAGE_BITMAP, 0, 0, LR_CREATEDIBSECTION);
BITMAP bmp;
GetObject(hbmp, sizeof bmp, &bmp);
COLORREF *buffer = new COLORREF [b.bmHeight * b.bmWidth];
HDC hdc = CreateCompatibleDC(0);
SelectObject(hdc, hbmp);
for (int x = 0; x < b.bmWidth; x++)
for (int y = 0; y < b.bmHeight; y++)
buffer[x+b.bmWidth*y] = GetPixel(hdc, x, bmp.bmHeight - y);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, bmp.bmWidth, bmp.bmHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, buffer);
DeleteDC(hdc);
DeleteObject(hbmp);
delete buffer;
... | |
So weit funktioniert auch alles. Es sieht nur schrecklich ineffizient aus, Devices zu erstellen (da LoadImage ein DeviceIndependentBitmap erstellt und es unnötig ist) und alle Texturen Pixel für Pixel zu kopieren. Mit GetDIBits hatte ich keinen Erfolg wegen der Parameter (was sind Scanlines?) und auch sonst scheint es kein GetAllPixels(hdc, buffer) zu geben.
Weiß jemand wie es richtig geht? |
|
|
|
 |
knivil
Mitglied
Benutzerprofil
Anmeldungsdatum: 11.02.2009
Beiträge: 5854
|
knivil Mitglied
00:20:57 27.06.2012 Titel: |
|
Zitieren |
Du machst es doch nur einmal beim Start der Anwendung, wen kuemmert es, wie lange das dauert.
Zu deinem eigentlichen Problem: Keine Ahnung! |
_________________ If it were not for laughter, there would be no Tao.
Sie können einen Beitrag nicht so schnell nach Ihrem letzten absenden, bitte warten Sie einen Augenblick.
|
|
 |
hustbaer
Mitglied
Benutzerprofil
Anmeldungsdatum: 27.10.2006
Beiträge: 16043
|
hustbaer Mitglied
00:44:16 27.06.2012 Titel: |
|
Zitieren |
| knivil schrieb: | | Du machst es doch nur einmal beim Start der Anwendung, wen kuemmert es, wie lange das dauert. |
Du hast dir den Code nichtmal angesehen, oder?
Texturdaten pixelweise mit GetPixel() kopieren, ja, klar, muss man so machen.
Weil's ja so lustig ist ne Minute zu warten wo es 1-2 Sekunden auch tun würden. |
_________________ "Let there be Licht..." http://lichttools.sourceforge.net/
Sehr cooles ASCII Spiel (leider nicht von mir): ASCII-Scramble - http://www.roskakori.at/ascii/
|
|
 |
dot
Mitglied
Benutzerprofil
Anmeldungsdatum: 20.05.2004
Beiträge: 5618
|
dot Mitglied
01:13:42 27.06.2012 Titel: |
|
Zitieren |
"Richtig": FindResource(), LoadResource() und LockResource() und dann die Bitmap einlesen... |
_________________ one point of view will never reveal the entire scene.
|
|
 |
knivil
Mitglied
Benutzerprofil
Anmeldungsdatum: 11.02.2009
Beiträge: 5854
|
knivil Mitglied
01:24:45 27.06.2012 Titel: |
|
Zitieren |
| Zitat: | | Du hast dir den Code nichtmal angesehen, oder? | Doch, aber ich habe nicht keine Vorstellung, wie lange die einzelnen Funktionsaufrufe benoetigen. |
_________________ If it were not for laughter, there would be no Tao.
Sie können einen Beitrag nicht so schnell nach Ihrem letzten absenden, bitte warten Sie einen Augenblick.
|
|
 |
dot
Mitglied
Benutzerprofil
Anmeldungsdatum: 20.05.2004
Beiträge: 5618
|
dot Mitglied
01:28:54 27.06.2012 Titel: |
|
Zitieren |
| knivil schrieb: | | Zitat: | | Du hast dir den Code nichtmal angesehen, oder? | Doch, aber ich habe nicht keine Vorstellung, wie lange die einzelnen Funktionsaufrufe benoetigen. |
Ok, in dem Fall: GetPixel() ist so ziemlich die langsamste Lösung, die du in der kompletten Win32 API finden wirst. Wenn du nur schnell die Farbe unterm Mauszeiger haben willst, dann geht's grad, aber zum "Einlesen" eines ganzen Bildes ist GetPixel() völlig unbrauchbar...
Das is wie einen Lastwagen voller Erde bestellen und die Erde dann mit einer Pinzette abladen, statt einfach runterzukippen... |
_________________ one point of view will never reveal the entire scene.
Zuletzt bearbeitet von dot am 01:30:54 27.06.2012, insgesamt 1-mal bearbeitet |
|
 |
hustbaer
Mitglied
Benutzerprofil
Anmeldungsdatum: 27.10.2006
Beiträge: 16043
|
hustbaer Mitglied
01:30:49 27.06.2012 Titel: |
|
Zitieren |
@dot
Ich glaube beim "die Bitmap einlesen" hat er Probleme, deswegen will er ja LoadImage() verwenden.
Ich hab' mit WinAPI und DIBs auch schon zu lange nix mehr gemacht, deswegen bin ich da auch nimmer so fit.
Eine Möglichkeit wäre vermutlich die "von Hand" rauszukopieren.
@nwp3
Wenn die Bitmap 24 Bit ist, dann ist das auch relativ einfach. In dem Fall zeigt BITMAP::bmBits auf ein Array aus "Scanlines". Jede Scanlines representiert eine Zeile im Bild und ist BITMAP::bmWidthBytes lang. BITMAP::bmWidthBytes ist dabei grösser oder gleich BITMAP::bmWidth * 3.
Die Pixel in einer Zeile liegen direkt hintereinander, jeweils 3 Byte pro Pixel (je ein Byte für Rot, Grün, Blau). Die Reihenfolge der Bytes weiss ich nimmer, entweder Rot, Grün, Blau oder Blau, Grün, Rot, das merk' ich mir nie. Einfach ausprobieren.
Sonst könntest du noch eine andere Library verwenden um die Bilder zu laden, das hätte dann auch den Vorteil dass du nicht auf BMP beschränkt bist sondern PNG, JPEG etc. verwenden kannst. Zur Auswahl stehen viele - GDI+, FreeImage, CImg, CxImage, OpenIL (aka DevIL) und noch viele mehr die mir nicht einfallen bzw. die ich nicht kenne.
Konkret abraten würde ich nur von OpenIL, da das Ding im Zusammenhang mit Multithreading problematisch ist.
GDI+ hätte den Vorteil dass es mit Windows mitkommt, d.h. man muss nix extra einbinden (Header Files sind Teil des DSK und die DLLs werden mit Windows mit installiert). Wenn dein Projekt erstmal sowieso nur für Windows gemacht wird würde ich also GDI+ empfehlen. Ist auch halbwegs angenehm in der Anwendung. |
_________________ "Let there be Licht..." http://lichttools.sourceforge.net/
Sehr cooles ASCII Spiel (leider nicht von mir): ASCII-Scramble - http://www.roskakori.at/ascii/
Zuletzt bearbeitet von hustbaer am 02:44:54 27.06.2012, insgesamt 1-mal bearbeitet |
|
 |
dot
Mitglied
Benutzerprofil
Anmeldungsdatum: 20.05.2004
Beiträge: 5618
|
dot Mitglied
01:34:28 27.06.2012 Titel: |
|
Zitieren |
| hustbaer schrieb: | @dot
Ich glaube beim "die Bitmap einlesen" hat er Probleme, deswegen will er ja LoadImage() verwenden. |
Ja, das glaub ich auch. In dem Fall sollte er sich aber einfach anschauen, wie das geht. Es ist nicht besonders schwer und dabei auch mehr oder weniger die maximal effiziente Lösung, da die Resource im Prinzip ein memory mapped File ist und man im Idealfall den über LockResource() gefundenen Pointer praktisch direkt an glTexImage2D() weiterreichen kann (glPixelStore() macht's möglich)...
EDIT: Die moderne built-in Lösung für alle möglichen Bildformate unter Windows wäre btw WIC.
Random Code Snippet, weil ichs grad rumliegen hab (ist zwar für D3D11, aber ich denk man kann damit was anfangen; Resource::Blob ist im Prinzip ein Wrapper um den ganzen LockResource() Kram; Wenn man mehrere Bilder laden will, sollte man die Factory und den FormatConverter etc. natürlich dazwischen aufheben):
| C++: | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 | #include <Shlwapi.h>
#include <wincodec.h>
// ...
com_ptr<ID3D11Texture2D> TextureAtlas::createAtlasTexture(ID3D11Device* device, const char* filename, size2ui& size)
{
Resource::Blob blob(filename, "IMAGE");
com_ptr<IStream> stream = SHCreateMemStream(reinterpret_cast<const BYTE*>(blob.get()), blob.size());
if (!stream)
throw std::runtime_error("unable to create resource stream");
com_ptr<IWICImagingFactory> factory;
if (FAILED(CoCreateInstance(CLSID_WICImagingFactory, nullptr, CLSCTX_INPROC_SERVER, IID_IWICImagingFactory, reinterpret_cast<void**>(&factory))))
throw std::runtime_error("unable to create WIC factory");
com_ptr<IWICBitmapDecoder> decoder;
if (FAILED(factory->CreateDecoderFromStream(stream, nullptr, WICDecodeMetadataCacheOnDemand, &decoder)))
throw std::runtime_error("error creating image decoder");
com_ptr<IWICBitmapFrameDecode> frame;
if (FAILED(decoder->GetFrame(0, &frame)))
throw std::runtime_error("unable to decode image");
com_ptr<IWICFormatConverter> format_converter;
if (FAILED(factory->CreateFormatConverter(&format_converter)))
throw std::runtime_error("unable to create format converter");
if (FAILED(format_converter->Initialize(frame, GUID_WICPixelFormat32bppRGBA, WICBitmapDitherTypeNone, nullptr, 0.0, WICBitmapPaletteTypeCustom)))
throw std::runtime_error("unable to convert pixel format");
if (FAILED(format_converter->GetSize(&size.width, &size.height)))
throw std::runtime_error("unable to determine image size");
D3D11_TEXTURE2D_DESC atlas_desc;
atlas_desc.Width = size.width;
atlas_desc.Height = size.height;
atlas_desc.MipLevels = 1;
atlas_desc.ArraySize = 1;
atlas_desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
atlas_desc.SampleDesc.Count = 1;
atlas_desc.SampleDesc.Quality = 0;
atlas_desc.Usage = D3D11_USAGE_DEFAULT;
atlas_desc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
atlas_desc.CPUAccessFlags = 0;
atlas_desc.MiscFlags = 0;
std::unique_ptr<BYTE[]> buffer(new BYTE[size.width * size.height * 4]);
if (FAILED(format_converter->CopyPixels(nullptr, size.width * 4, size.width * size.height * 4, buffer.get())))
throw std::runtime_error("unable to read pixel data");
D3D11_SUBRESOURCE_DATA data;
data.pSysMem = buffer.get();
data.SysMemPitch = size.width * 4;
com_ptr<ID3D11Texture2D> atlas_tex;
if (FAILED(device->CreateTexture2D(&atlas_desc, &data, &atlas_tex)))
throw std::runtime_error("unable to create atlas texture");
return atlas_tex;
} | | |
_________________ one point of view will never reveal the entire scene.
Zuletzt bearbeitet von dot am 01:59:26 27.06.2012, insgesamt 11-mal bearbeitet |
|
 |
hustbaer
Mitglied
Benutzerprofil
Anmeldungsdatum: 27.10.2006
Beiträge: 16043
|
hustbaer Mitglied
02:41:48 27.06.2012 Titel: |
|
Zitieren |
| dot schrieb: | | ... im Idealfall den über LockResource() gefundenen Pointer praktisch direkt an glTexImage2D() weiterreichen kann (glPixelStore() macht's möglich)... |
Kann man den GL_UNPACK_ALIGNMENT Wert verwenden damit OpenGL das 4-Byte Row-Alignment von BMP Files "versteht"?
Das ginge dann ja noch.
Was ich aber nicht verstehe, ist, warum es da keinen GL_UNPACK_BYTES_PER_ROW Wert gibt. (Bzw. wieso GL_UNPACK_ROW_LENGTH nicht einfach in Bytes ist)
So ziemlich alle Image-Libraries die ich kenne haben einen "bytes per line" Wert im Interface. D.h. in dem Fall müsste man erst wieder in einen Zwischenpuffer kopieren, oder übersehe ich was? |
_________________ "Let there be Licht..." http://lichttools.sourceforge.net/
Sehr cooles ASCII Spiel (leider nicht von mir): ASCII-Scramble - http://www.roskakori.at/ascii/
Zuletzt bearbeitet von hustbaer am 02:47:15 27.06.2012, insgesamt 1-mal bearbeitet |
|
 |
|
Nächstes Thema anzeigen
Vorheriges Thema anzeigen
Sie können Beiträge in dieses Forum schreiben. Sie können auf Beiträge in diesem Forum antworten. Sie können Ihre Beiträge in diesem Forum nicht bearbeiten. Sie können Ihre Beiträge in diesem Forum nicht löschen. Sie können an Umfragen in diesem Forum nicht mitmachen.
|
|
|
|
|