Loading PNG files as OpenGL textures

classic Classic list List threaded Threaded
33 messages Options
12
Reply | Threaded
Open this post in threaded view
|

Loading PNG files as OpenGL textures

Ryan Joseph
I’m coming from the Mac and I’d like to replace some code that loads .png files into OpenGL textures using Apple libraries that aren’t cross platform compatible.

Does the FPC RTL contain any units for loading PNG files as bitmaps that I could extract the pixel data from and make textures for? I was using .png but I could use TIFF also or any other format which has an alpha channel.

I’ve found Vampyre Imaging Library but a) it’s huge and does far more than I need and b) I haven’t been able to find all it’s various dependancies, some of which use a newer version of OpenGL I’m not using. A small simple solution would be best if that’s possible.

Thanks.

Regards,
        Ryan Joseph

_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Loading PNG files as OpenGL textures

Michael Van Canneyt


On Sun, 11 Oct 2015, Ryan Joseph wrote:

> I’m coming from the Mac and I’d like to replace some code that loads .png files into OpenGL textures using Apple libraries that aren’t cross platform compatible.
>
> Does the FPC RTL contain any units for loading PNG files as bitmaps that I could extract the pixel data from and make textures for? I was using .png but I could use TIFF also or any other format which has an alpha channel.
>
> I’ve found Vampyre Imaging Library but a) it’s huge and does far more than I need and b) I haven’t been able to find all it’s various dependancies, some of which use a newer version of OpenGL I’m not using. A small simple solution would be best if that’s possible.

You can use the fpimage and fpreadpng units.
Check the packages/fcl-image sources.
There is a small demo program that shows how to do it.

Michael.
_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Loading PNG files as OpenGL textures

Ryan Joseph

> On Oct 11, 2015, at 1:53 PM, Michael Van Canneyt <[hidden email]> wrote:
>
> You can use the fpimage and fpreadpng units. Check the packages/fcl-image sources. There is a small demo program that shows how to do it.

I see you can load an image as TFPCustomImage with TFPCustomImageReader then there is a canvas class (TFPCustomCanvas) which I assume I could draw the TFPCustomImage into and expose the pixels as a pointer to memory which I could then use to make the OpenGL texture.

I’m not able to see the references for TFPCustomCanvas in particular so I’m not sure how this would work. There is the guide I found but is there any more complete examples I could look at? Too much is missing to understand exactly how all these classes work together.

http://wiki.freepascal.org/fcl-image

Thanks.


Regards,
        Ryan Joseph

_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Loading PNG files as OpenGL textures

Graeme Geldenhuys-6
In reply to this post by Ryan Joseph
On 2015-10-11 05:29, Ryan Joseph wrote:
> Does the FPC RTL contain any units for loading PNG files as bitmaps

As Michael mentioned, fpimage and fpreadpng can do this. Here is how I
use it to populate fpGUI's TfpgImage class with image data.

https://github.com/graemeg/fpGUI/blob/develop/src/corelib/fpg_imgfmt_png.pas


Regards,
  - Graeme -

--
fpGUI Toolkit - a cross-platform GUI toolkit using Free Pascal
http://fpgui.sourceforge.net/

My public PGP key:  http://tinyurl.com/graeme-pgp
_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Loading PNG files as OpenGL textures

Anthony Walter-3
In reply to this post by Ryan Joseph
You can write an interface to the quartz libs and have them load or save a variety of image formats such as bmp, jpg, gif, and png. I've done this with OpenGL on OSX with Free Pascal and I look for the code and post it to git sometime, maybe even Today.


_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Loading PNG files as OpenGL textures

Ryan Joseph
In reply to this post by Graeme Geldenhuys-6

> On Oct 11, 2015, at 10:45 PM, Graeme Geldenhuys <[hidden email]> wrote:
>
> As Michael mentioned, fpimage and fpreadpng can do this. Here is how I
> use it to populate fpGUI's TfpgImage class with image data.
>
> https://github.com/graemeg/fpGUI/blob/develop/src/corelib/fpg_imgfmt_png.pas

But can I then access the pixel data so I can hand it to glTexImage2D and make the texture? I think the FPC RTL classes on http://wiki.freepascal.org/fcl-image would do this also but again it’s not clear how to get the pixel data for glTexImage2D

Thanks.

Regards,
        Ryan Joseph

_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Loading PNG files as OpenGL textures

Ryan Joseph
In reply to this post by Anthony Walter-3

> On Oct 12, 2015, at 3:46 AM, Anthony Walter <[hidden email]> wrote:
>
> You can write an interface to the quartz libs and have them load or save a variety of image formats such as bmp, jpg, gif, and png. I've done this with OpenGL on OSX with Free Pascal and I look for the code and post it to git sometime, maybe even Today.
>

This is what I do on Mac but I wanted to make some cross platform code so I need to kill lots of this Apple stuff.

Basically I loaded the image from file, made a CGImage wrapper to it (which understands PNG) and then draw it into a CGContext (like a bitmap canvas) then extracted the pixel data from the bitmap for use with glTexImage2D.

The FPC RTL on http://wiki.freepascal.org/fcl-image seems to do this but I don’t see the interface for TFPCustomCanvas so I don’t know if it’s possible to access the pixel data.

There’s code at http://www.sulaco.co.za/opengl_project_BMP_JPG_TGA_texture_loader.htm which follows a similar procedure but it’s Delphi and I couldn’t get it to run on FPC on my anyways.


Regards,
        Ryan Joseph

_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Loading PNG files as OpenGL textures

Anthony Walter-3
In reply to this post by Ryan Joseph
Okay, I looked at my Darwin sources which use minimal wrappers around the inbuilt Quartz classes to handle image loading, saving, and pixel data access.

To load any image (png, jpg, bmp, gif, ect) you use a CGImageSourceRef object.

From file:

ImageSource := CGImageSourceCreateWithURL(UrlRefCreateFromFileSystem(FileName).Handle, nil);

From a stream:

ImageSource := CGImageSourceCreateWithData(DataRefCreate(Stream).Handle, nil);

Then to access image data you need to create a bitmap context and tell the image source to populate the bitmap. My bitmap context create looks like this, which should give you a head start:

constructor TBitmapContextRef.Create(Image: IImageRef; ColorSpace: TColorSpace = colorBGRA);
var
  R: CGRect;
begin
  Create(Image.Width, Image.Height);
  R.origin.x := 0;
  R.origin.y := 0;
  R.size.width := Image.Width;
  R.size.height := Image.Height;
  CGContextDrawImage(Handle, R, Image.Handle);
end;

constructor TBitmapContextRef.Create(Width, Height: Integer; ColorSpace: TColorSpace = colorBGRA);
var
  PixelBytes: UIntPtr;
  C: CGColorSpaceRef;
  A: CGImageAlphaInfo;
  R: CGContextRef;
begin
  inherited Create(nil);
  PixelBytes := Width * Height * 4;
  FPixels := GetMem(PixelBytes);
  FillZero(FPixels^, PixelBytes);
  C := CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB);
  A := ColorSpaceToAlpha(ColorSpace);
  R := CGBitmapContextCreate(FPixels, Width, Height, 8, Width * 4, C, A);
  CGColorSpaceRelease(C);
  HandleOwn(R);
end;


You might be able to figure the rest out from there by yourself. If instead of figuring things out for yourself, you might to use want the Darwin graphics part of my library. For this you'll need two of my unit files:

The first unit wraps the ref object management pattern used by Darwin and includes plus a few extra helper routines.
The second unit wraps the Quartz 2D objects and also includes a few simple helper routines.

Let me know if you want to contribute and I can upload some code to a repository on github. But please I would ask that if you have any improvements to my code (such as wrapping more classes), that you share those changes with everyone using the same github repository. I'll need you github account names add you guys as contributors.

_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Loading PNG files as OpenGL textures

Ryan Joseph

> On Oct 12, 2015, at 9:47 AM, Anthony Walter <[hidden email]> wrote:
>
> Okay, I looked at my Darwin sources which use minimal wrappers around the inbuilt Quartz classes to handle image loading, saving, and pixel data access.
>


I have a good working implementation of loading OpenGL textures on Mac but I want to replace it with a cross platform solution that would run on Windows/Linux also that doesn’t rely on CoreGraphics libraries.

I think the classes in FPimage/FPCanvas from the RTL do this already so why do we need to use CoreGraphics and Apple-only libraries?

Regards,
        Ryan Joseph

_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Loading PNG files as OpenGL textures

Anthony Walter-3
I tend to use OS core elements or well known open source libraries for a few reasons.

1) less code compiled and small programs
2) superior performance
3) more features

Example: When loading hundreds of textures you bet the Apple Quartz graphics libs are faster and more reliable. Same goes to WIC on Windows and Cario on linux.

Example: When processing XML, the power of the MSXML parser is going to be both lighter and faster than any other implementation on Windows. It's also includes the ability to do stylesheet transforms.

Example: When rendering video frames to textures you bet I am going to use ffmpeg libs rather than some native implementation.

This isn't meant to dissuade people from writing native implementations in Pascal. I wrote and maintain a game engine, but even so it's just a friendly interface to SDL2, OpenGL, SSL, XML, FFMPEG, and System graphics libs. I have no problem using that stuff when I write Pascal applications, and that's where I think Pascal excels, which is making friendly OO encapsulation wrappers around stuff using PME (objects with properties, methods, and events).

http://www.getlazarus.org/videos/crossgraphics/ (Quartz,Cairo,Direct2D,GdiPlus wrappers)


_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Loading PNG files as OpenGL textures

Ryan Joseph

> On Oct 12, 2015, at 10:57 AM, Anthony Walter <[hidden email]> wrote:
>
> I tend to use OS core elements or well known open source libraries for a few reasons.
>
> 1) less code compiled and small programs
> 2) superior performance
> 3) more features
>
> Example: When loading hundreds of textures you bet the Apple Quartz graphics libs are faster and more reliable. Same goes to WIC on Windows and Cario on linux.
>

So your advice would be to keep my Apple libraries I use on Mac and something else for Windows? What would be the equivalent libraries on Windows for CGImage and CGBitmapContext be?

Loading PNG’s into a bitmap seems like a pretty thing for single unit but I could do the native route also if it was easier. I just wanted to get started on cross platform basically and I’m looking for the easiest strategy.


Here’s an example function to load a texture on Mac (given a CGImageRef you load from CGImageCreateWithURL). Pretty easy using Apple frameworks and I assume those would have some FPC RTL equivalents.

function GenerateTextureFromImage (image: CGImageRef): GLuint;
var
        textureWidth, textureHeight: integer;
        textureName: GLuint;
        bounds: CGRect;
        bitmapData: pointer;
        colorSpace: CGColorSpaceRef;
        context: CGContextRef;
begin
        result := 0;
       
        // generate texture
        textureWidth := CGImageGetWidth(image);
        textureHeight := CGImageGetHeight(image);
        bounds := CGRectMake(0, 0, textureWidth, textureHeight);
        bitmapData := GetMem(textureHeight * (textureWidth * 4));
       
        if bitmapData <> nil then
                begin
                        colorSpace := CGColorSpaceCreateDeviceRGB;
                  context := CGBitmapContextCreate(bitmapData, textureWidth, textureHeight, 8, 4 * textureWidth, colorSpace, kCGImageAlphaPremultipliedLast);

                        if context <> nil then
                                begin
                                        CGContextFlipVertically(context, bounds);
                                        CGContextClearRect(context, bounds);
                                        CGContextDrawImage(context, bounds, image);
                                       
                                        glGenTextures(1, @result);
                                        glBindTexture(GL_TEXTURE_2D, result);
                                        glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE);
                                        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, textureWidth, textureHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, bitmapData);

                                        //writeln('generated texture ', result, ' (', textureWidth, 'x', textureHeight,')');
                                        CGContextRelease(context);
                                end;

                        FreeMemory(bitmapData);
                        CFRelease(colorSpace);
                end;
end;


Regards,
        Ryan Joseph

_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Loading PNG files as OpenGL textures

Michael Van Canneyt
In reply to this post by Anthony Walter-3


On Sun, 11 Oct 2015, Anthony Walter wrote:

> I tend to use OS core elements or well known open source libraries for a
> few reasons.
>
> 1) less code compiled and small programs
> 2) superior performance
> 3) more features

1. is not true for image code if you're using lazarus. The code will already be there.
2. May be true, depends on what it is.
3. May be true, depends on what it is.

But the downside is that you will need to write glue code for all libraries.
Which can be a real pain for cross-platform apps. Look at lazarus itself.
As a consequence your application may behave differently on each OS.
(XML is a sore point there, or Unicode)

So I would never recommend your route, unless there is no native pascal implementation.

For the specific case of image loading, only 2 holds true;
so unless you really need great performance I see no reason to go this route.

Michael.
_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Loading PNG files as OpenGL textures

Anthony Walter-3
In reply to this post by Ryan Joseph
I would abstract all platform specific code. I define a encapsulating set of interfaces for any platform specific stuff. Interface types also provide a nice layer of abstraction.

In such a way I have at least IDocument, IBitmap, IAudio, and IVideo; where the last three type define methods for loading and decompressing images, audio, and video frames in that order. Then for each platform I map the interfaces to whatever native OS functions work best (or perhaps easiest). On OSX I use the QuickTime objects to decode video and on Linux it would be ffmpeg. On Windows I use MSXML parser, on Linux it would be libxml2 (I use my own libxml wrapper which has better xpath encapsulation), and so on.

Interfaces go into standard units such as YourCompany.Graphics, and implementations go into YourCompany.Platform.Darwin.Graphics or YourCompany.Platform.Linux.Graphics. Then in code I only reference YourCompany.Graphics, which has code like this:  

unit YourCompany.Graphics;

type IBitmap = interface ... end;

function CreateBitmap(const FileName: string): IBitmap; overload;
function CreateBitmap(Stream: TStream): IBitmap; overload;

So then to your example. In my engine I have a TTextures asset manager class (exposed as a singleton):

TTextures = class(TAssetManager)
  procedure Add(const FileName: string; const Name: string); overload;
  procedure Add(Stream: TStream; const Name: string); overload;
  procedure Bind(const Name: string);
  property Texture[Name: string]: TTexture read GetTexture; default;
  ...

function Textures: TTextures;

The actual TTextures.Add methods uses an IBitmap interface which can to load, save, give dimensions, and provide access to pixel data.

procedure TTextures.Add(Stream: TStream; const Name: string);
var
  B: IBitmap;
  T: Texture;
begin
  if Texture[Name] <> nil then Exit;
  B := CreateBitmap(Stream);
  T := TTexture.Create;
  T.Name := Name;
  T.Width := B.Width;
  T.Height := B.Height;
  glGenTextures(1, @T.FId);
  // B.Pixels has the pixel data which will be freed with the interface
  ...
end;

You get the idea I hope. 

For stuff which might need a different implementation on platforms: define interfaces.
For stuff which is unaffected by platforms, encapsulate that using types like records, classes, enums, and sets.


_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Loading PNG files as OpenGL textures

Anthony Walter-3
In reply to this post by Michael Van Canneyt
Michae

_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Loading PNG files as OpenGL textures

Michael Van Canneyt
In reply to this post by Ryan Joseph


On Sun, 11 Oct 2015, Ryan Joseph wrote:

>
>> On Oct 11, 2015, at 1:53 PM, Michael Van Canneyt <[hidden email]> wrote:
>>
>> You can use the fpimage and fpreadpng units. Check the packages/fcl-image sources. There is a small demo program that shows how to do it.
>
> I see you can load an image as TFPCustomImage with TFPCustomImageReader then there is a canvas class (TFPCustomCanvas) which I assume I could draw the TFPCustomImage into and expose the pixels as a pointer to memory which I could then use to make the OpenGL texture.

You don't need TFPCustomCanvas.

TFPCustomImage is an abstract image class, it provides no storage for the data.
It can have several descendants such as TFPMemoryImage and TFPCompactImgRGBA8Bit.
If you just want the raw data, create a TFPMemoryImage and access the Colors array property.

Michael.
_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Loading PNG files as OpenGL textures

Anthony Walter-3
In reply to this post by Michael Van Canneyt
Michael,

I've written a whole lot of OpenGL demos (demoscene) using both C and Pascal. I can tell you there is a huge difference between the Delphi image decompression performance and the performance of the image libs ones provided by the OS (WIC and Quartz especially). The OS imaging libs are orders of magnitudes faster than TFPImage, especially when it comes to loading 100s of textures during the start of a demo or game. Speed aside, all the OS imaging libs also provide vastly superior image manipulation including scaling, blending, and all support matrix color operations on pixels.

I find it ridiculous to reinvent a worse version the wheel in Pascal. Yeah sure I love Pascal, but mostly for the friendly wrappers it creates out of everything, which was how Delphi started.

Button.Caption := 'Hello World';


_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Loading PNG files as OpenGL textures

Michael Van Canneyt


On Mon, 12 Oct 2015, Anthony Walter wrote:

> Michael,
>
> I've written a whole lot of OpenGL demos (demoscene) using both C and
> Pascal. I can tell you there is a huge difference between the Delphi image
> decompression performance and the performance of the image libs ones
> provided by the OS (WIC and Quartz especially). The OS imaging libs are
> orders of magnitudes faster than TFPImage, especially when it comes to
> loading 100s of textures during the start of a demo or game. Speed aside,
> all the OS imaging libs also provide vastly superior image manipulation
> including scaling, blending, and all support matrix color operations on
> pixels.

As I said: performance is the only reason IMHO.
If all you need to do is load an image to display it somewhere,
then your method is complete overkill and not worth the hassle.

And everyone is reinventing the wheel anyway, every gaming library
I've encountered out there reinvents all the image routines, be it
as wrappers or native.

The main reason TFPImage exists is to be free of external dependencies.

Michael.
_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Loading PNG files as OpenGL textures

Graeme Geldenhuys-6
In reply to this post by Ryan Joseph
On 2015-10-12 01:21, Ryan Joseph wrote:
> But can I then access the pixel data so I can hand it to glTexImage2D and make the texture?

Yes, look at line 61. Simply use the Colors[] array property.

The code I suggested was not meant as a complete implementation for you,
but as an example of how to use the fpImage and FPReadPNG units. Like I
said, once the PNG file is loaded, use the Colors[] array property to
access the pixel data.

Regards,
  - Graeme -

--
fpGUI Toolkit - a cross-platform GUI toolkit using Free Pascal
http://fpgui.sourceforge.net/

My public PGP key:  http://tinyurl.com/graeme-pgp
_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Loading PNG files as OpenGL textures

Graeme Geldenhuys-6
In reply to this post by Anthony Walter-3
On 2015-10-12 08:26, Anthony Walter wrote:
> The OS imaging libs are orders of magnitudes faster than TFPImage,
> especially when it comes to loading 100s of textures during the start
> of a demo or game.

Indeed, FPImage is not very fast... This was also extensively proven and
compared against many other image libraries. The convenience is that you
have NO 3rd party dependencies and your code is 100% cross-platform. So
you need to trade up the options. If you one need to load a handful of
textures, then FPImage will do perfectly and you probably wouldn't
notice any speed penalties.

http://members.upc.nl/h.speksnijder4/software/fpGUI/imagesread.html
http://members.upc.nl/h.speksnijder4/software/fpGUI/pngloader.html


> Speed aside, all the OS imaging libs also provide vastly superior
> image manipulation including scaling, blending, and all support
> matrix color operations on pixels.

Sorry, but it is definitely not that black & white. I know of many
libraries that perform better than native OS libraries, and have better
rendering results too. AGG vs GDI+ is one such example - the AGG library
renders much better anti-aliasing and font rendering than anything
Microsoft has created. See the documentation and research sections on
this website:

  http://antigrain.com/


Regards,
  - Graeme -

--
fpGUI Toolkit - a cross-platform GUI toolkit using Free Pascal
http://fpgui.sourceforge.net/

My public PGP key:  http://tinyurl.com/graeme-pgp
_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Loading PNG files as OpenGL textures

Reimar Grabowski
In reply to this post by Ryan Joseph
On Sun, 11 Oct 2015 16:36:56 +0700
Ryan Joseph <[hidden email]> wrote:

> is there any more complete examples I could look at?

http://sourceforge.net/p/asmoday/code/HEAD/tree/trunk/asmtypes.pas#l203

Not an example but a straightforward texture class implementation based on TFPCustomImage. Should work as is for bmp, jpg, png, tga, xpm, if not drop me a line.

hih
R.
_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
12