Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F123441
PNGLoader.cpp
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Award Token
Size
10 KB
Referenced Files
None
Subscribers
None
PNGLoader.cpp
View Options
#include
<echo/Resource/PNGLoader.h>
#include
<echo/FileSystem/File.h>
#include
<iostream>
#include
<bitset>
//#define PNGLOADER_DEBUG_OUTPUT
#define PNG_SETJMP_NOT_SUPPORTED
#include
<png.h>
// png.h should #define the libpng version numbers. Test for oldest supported version:
BOOST_STATIC_ASSERT
(
PNG_LIBPNG_VER_MAJOR
==
1
);
BOOST_STATIC_ASSERT
(
PNG_LIBPNG_VER_MINOR
>=
2
);
// TODO: figure out the exact version number where where the interface changed
#define NEWER_INTERFACE_LIBPNG_VER_MINOR 6
namespace
Echo
{
class
PNGLoader
::
Implementation
{
public
:
Implementation
()
:
mWidth
(
0
),
mHeight
(
0
),
mBytesPerPixel
(),
mFormat
(
Texture
::
Formats
::
UNKNOWN
),
mCurrentRow
(),
#if PNG_LIBPNG_VER_MINOR < NEWER_INTERFACE_LIBPNG_VER_MINOR
mRowPointers
(),
mPNGPtr
(),
mInfoPtr
(),
#endif
mFile
(
nullptr
)
{}
u32
mWidth
;
u32
mHeight
;
u32
mBytesPerPixel
;
Texture
::
Format
mFormat
;
u32
mCurrentRow
;
#if PNG_LIBPNG_VER_MINOR < NEWER_INTERFACE_LIBPNG_VER_MINOR
png_bytepp
mRowPointers
;
png_structp
mPNGPtr
;
png_infop
mInfoPtr
;
#else
// TODO: Add new interface code here
#endif
File
*
mFile
;
};
#if PNG_LIBPNG_VER_MINOR < NEWER_INTERFACE_LIBPNG_VER_MINOR
png_voidp
PngAlloc
(
png_structp
p
,
png_size_t
s
)
{
png_voidp
d
=
(
png_voidp
)
(
new
u8
[
s
]);
return
d
;
}
void
PngFree
(
png_structp
p
,
png_voidp
s
)
{
u8
*
u8p
=
reinterpret_cast
<
u8
*>
(
s
);
delete
[]
u8p
;
}
void
PngRead
(
png_structp
png_ptr
,
png_bytep
data
,
png_size_t
length
)
{
voidp
read_io_ptr
=
png_get_io_ptr
(
png_ptr
);
if
(
read_io_ptr
)
{
File
&
file
=
*
reinterpret_cast
<
File
*>
(
read_io_ptr
);
file
.
Read
(
data
,
1
,
(
u32
)
length
);
}
}
#else
// TODO: Add new interface code here
#endif
PNGLoader
::
PNGLoader
()
:
mImplementation
(
new
PNGLoader
::
Implementation
())
{}
PNGLoader
::~
PNGLoader
()
{}
bool
PNGLoader
::
ProcessFile
(
File
&
textureFile
)
{
mImplementation
->
mFile
=
nullptr
;
mImplementation
->
mCurrentRow
=
0
;
if
(
!
textureFile
.
IsOpen
())
{
std
::
cout
<<
"PNGLoader: Error input file is not open: "
<<
textureFile
.
GetActualFileName
()
<<
std
::
endl
;
return
false
;
}
std
::
cout
<<
"LoadPNG: "
<<
textureFile
.
GetActualFileName
()
<<
std
::
endl
;
u8
header
[
8
];
//Read first 8 bytes - if we couldn't then that really sucks
if
(
textureFile
.
Read
((
void
*
)
(
&
header
),
1
,
8
)
<
8
)
{
std
::
cout
<<
"PNGLoader: Failed to read in first 8 bytes: "
<<
textureFile
.
GetActualFileName
()
<<
std
::
endl
;
return
0
;
}
//Check the file is a PNG
const
bool
is_png
=
#if PNG_LIBPNG_VER_MINOR < NEWER_INTERFACE_LIBPNG_VER_MINOR
!
png_sig_cmp
(
header
,
0
,
8
);
#else
false
;
// TODO: Add new interface code here
std
::
cout
<<
"PNGLoader: libpng "
PNG_LIBPNG_VER_STRING
" not implemented yet!"
<<
std
::
endl
;
#endif
if
(
!
is_png
)
{
std
::
cout
<<
"PNGLoader: File is not a PNG file: "
<<
textureFile
.
GetActualFileName
()
<<
std
::
endl
;
return
0
;
}
//Allocate memory for the png stuct and the info struct
mImplementation
->
mPNGPtr
=
#if PNG_LIBPNG_VER_MINOR < NEWER_INTERFACE_LIBPNG_VER_MINOR
png_create_read_struct_2
(
PNG_LIBPNG_VER_STRING
,
0
,
0
,
0
,
0
,
PngAlloc
,
PngFree
);
#else
nullptr
;
// TODO: Add new interface code here
#endif
if
(
!
mImplementation
->
mPNGPtr
)
{
std
::
cout
<<
"PNGLoader: png_create_read_struct: "
<<
textureFile
.
GetActualFileName
()
<<
std
::
endl
;
return
0
;
}
mImplementation
->
mInfoPtr
=
#if PNG_LIBPNG_VER_MINOR < NEWER_INTERFACE_LIBPNG_VER_MINOR
png_create_info_struct
(
mImplementation
->
mPNGPtr
);
#else
nullptr
;
// TODO: Add new interface code here
#endif
if
(
!
mImplementation
->
mInfoPtr
)
{
#if PNG_LIBPNG_VER_MINOR < NEWER_INTERFACE_LIBPNG_VER_MINOR
png_destroy_read_struct
(
&
mImplementation
->
mPNGPtr
,
(
png_infopp
)
0
,
(
png_infopp
)
0
);
#else
// TODO: Add new interface code here
#endif
std
::
cout
<<
"PNGLoader: png_create_info_struct:1: "
<<
textureFile
.
GetActualFileName
()
<<
std
::
endl
;
return
0
;
}
mImplementation
->
mFile
=
&
textureFile
;
#if PNG_LIBPNG_VER_MINOR < NEWER_INTERFACE_LIBPNG_VER_MINOR
png_set_read_fn
(
mImplementation
->
mPNGPtr
,
mImplementation
->
mFile
,
PngRead
);
png_set_sig_bytes
(
mImplementation
->
mPNGPtr
,
8
);
png_read_png
(
mImplementation
->
mPNGPtr
,
mImplementation
->
mInfoPtr
,
PNG_TRANSFORM_IDENTITY
,
NULL
);
png_byte
colourType
=
png_get_color_type
(
mImplementation
->
mPNGPtr
,
mImplementation
->
mInfoPtr
);
if
(
colourType
==
PNG_COLOR_TYPE_RGB_ALPHA
)
{
png_set_swap_alpha
(
mImplementation
->
mPNGPtr
);
}
//We only support 8 and channels at the moment
u32
bitDepth
=
png_get_bit_depth
(
mImplementation
->
mPNGPtr
,
mImplementation
->
mInfoPtr
);
if
(
bitDepth
==
16
)
{
png_set_strip_16
(
mImplementation
->
mPNGPtr
);
}
mImplementation
->
mRowPointers
=
png_get_rows
(
mImplementation
->
mPNGPtr
,
mImplementation
->
mInfoPtr
);
mImplementation
->
mWidth
=
png_get_image_width
(
mImplementation
->
mPNGPtr
,
mImplementation
->
mInfoPtr
);
mImplementation
->
mHeight
=
png_get_image_height
(
mImplementation
->
mPNGPtr
,
mImplementation
->
mInfoPtr
);
mImplementation
->
mBytesPerPixel
=
png_get_channels
(
mImplementation
->
mPNGPtr
,
mImplementation
->
mInfoPtr
);
// PNG only supports 8 bit and 16bit channels. There is a chunk called sBIT which
// can define which bits you consider significant. These can be used to shift the
// bits to make them relevant. libpng will do this for you if you enable it.
// I created a R4G4B4A4 image and ImageMagick was able to detect 4bits per channel
// however when I load the image here I cannot work out a way to find this out.
// I've inspected all of the info structure.
// To test the loading of the R4G4B4A4 file I forced the bitDepth to 8. This doesn't
// perform the shifts automatically for you but the loader code here will deal with
// the 8 bit to 4 bit conversion.
if
(
bitDepth
==
8
)
{
if
(
colourType
==
PNG_COLOR_TYPE_RGB_ALPHA
)
{
mImplementation
->
mFormat
=
Texture
::
Formats
::
R8G8B8A8
;
}
else
if
(
colourType
==
PNG_COLOR_TYPE_RGB
)
{
if
(
mImplementation
->
mBytesPerPixel
==
3
)
{
mImplementation
->
mFormat
=
Texture
::
Formats
::
R8G8B8
;
}
else
{
mImplementation
->
mFormat
=
Texture
::
Formats
::
R8G8B8X8
;
}
}
else
{
std
::
cout
<<
"PNGLoader Error: Unsupported PNG Format"
<<
std
::
endl
;
return
false
;
}
}
else
if
(
bitDepth
==
4
&&
(
colourType
==
PNG_COLOR_TYPE_RGB_ALPHA
))
{
mImplementation
->
mFormat
=
Texture
::
Formats
::
R4G4B4A4
;
}
else
{
//PNG_COLOR_TYPE_GRAY_ALPHA
//PNG_COLOR_TYPE_GRAY
//PNG_COLOR_TYPE_PALETTE
std
::
cout
<<
"PNGLoader Error: Unsupported PNG Format"
<<
std
::
endl
;
return
false
;
}
#ifdef PNGLOADER_DEBUG_OUTPUT
png_infop
info
=
mImplementation
->
mInfoPtr
;
std
::
cout
<<
"
\t
width: "
<<
info
->
width
<<
std
::
endl
;
std
::
cout
<<
"
\t
height: "
<<
info
->
height
<<
std
::
endl
;
std
::
cout
<<
"
\t
valid: "
<<
info
->
valid
<<
std
::
endl
;
std
::
cout
<<
"
\t
rowbytes: "
<<
info
->
rowbytes
<<
std
::
endl
;
std
::
cout
<<
"
\t
num_palette: "
<<
info
->
num_palette
<<
std
::
endl
;
std
::
cout
<<
"
\t
num_trans: "
<<
info
->
num_trans
<<
std
::
endl
;
std
::
cout
<<
"
\t
bit_depth: "
<<
(
int
)
info
->
bit_depth
<<
std
::
endl
;
std
::
cout
<<
"
\t
color_type: "
<<
(
int
)
info
->
color_type
<<
std
::
endl
;
std
::
cout
<<
"
\t
compression_type: "
<<
(
int
)
info
->
compression_type
<<
std
::
endl
;
std
::
cout
<<
"
\t
filter_type: "
<<
(
int
)
info
->
filter_type
<<
std
::
endl
;
std
::
cout
<<
"
\t
interlace_type: "
<<
(
int
)
info
->
interlace_type
<<
std
::
endl
;
std
::
cout
<<
"
\t
channels: "
<<
(
int
)
info
->
channels
<<
std
::
endl
;
std
::
cout
<<
"
\t
pixel_depth: "
<<
(
int
)
info
->
pixel_depth
<<
std
::
endl
;
std
::
cout
<<
"
\t
spare_byte: "
<<
(
int
)
info
->
spare_byte
<<
std
::
endl
;
std
::
cout
<<
"
\t
sig_bit R:"
<<
(
int
)
info
->
sig_bit
.
red
<<
std
::
endl
;
std
::
cout
<<
"
\t
sig_bit G:"
<<
(
int
)
info
->
sig_bit
.
green
<<
std
::
endl
;
std
::
cout
<<
"
\t
sig_bit B:"
<<
(
int
)
info
->
sig_bit
.
blue
<<
std
::
endl
;
std
::
cout
<<
"
\t
sig_bit A:"
<<
(
int
)
info
->
sig_bit
.
alpha
<<
std
::
endl
;
std
::
cout
<<
"
\t
Format:"
<<
mImplementation
->
mFormat
<<
std
::
endl
;
#endif
return
true
;
#else
// TODO: Add new interface code here
return
false
;
#endif
}
std
::
string
PNGLoader
::
GetFileExtension
()
const
{
return
"png"
;
}
u32
PNGLoader
::
GetWidth
()
const
{
return
mImplementation
->
mWidth
;
}
u32
PNGLoader
::
GetHeight
()
const
{
return
mImplementation
->
mHeight
;
}
Texture
::
Format
PNGLoader
::
GetFormat
()
const
{
return
mImplementation
->
mFormat
;
}
bool
PNGLoader
::
GetLoadInverted
()
const
{
return
false
;
}
void
PNGLoader
::
ReadLine
(
u8
*
lineStart
,
u32
maxLength
)
{
if
(
!
mImplementation
->
mFile
||
!
lineStart
)
{
return
;
}
if
(
mImplementation
->
mFormat
==
Texture
::
Formats
::
R4G4B4A4
)
{
u32
widthBytes
=
std
::
min
(
mImplementation
->
mWidth
*
mImplementation
->
mBytesPerPixel
,
maxLength
);
for
(
u32
c
=
0
,
di
=
0
;
di
<
widthBytes
;
c
+=
4
,
di
+=
2
)
{
//Convert to 4bits per channel
#ifdef ECHO_BIG_ENDIAN
// We have to deal with big endian byte difference here because File can't do it automatically
// since PNG files are compressed. Byte order
lineStart
[
di
]
=
(
mImplementation
->
mRowPointers
[
mImplementation
->
mCurrentRow
][
c
]
&
0xF0
)
|
(
mImplementation
->
mRowPointers
[
mImplementation
->
mCurrentRow
][
c
+
1
]
>>
4
);
lineStart
[
di
+
1
]
=
(
mImplementation
->
mRowPointers
[
mImplementation
->
mCurrentRow
][
c
+
2
]
&
0xF0
)
|
(
mImplementation
->
mRowPointers
[
mImplementation
->
mCurrentRow
][
c
+
3
]
>>
4
);
#else
lineStart
[
di
]
=
(
mImplementation
->
mRowPointers
[
mImplementation
->
mCurrentRow
][
c
+
2
]
&
0xF0
)
|
(
mImplementation
->
mRowPointers
[
mImplementation
->
mCurrentRow
][
c
+
3
]
>>
4
);
lineStart
[
di
+
1
]
=
(
mImplementation
->
mRowPointers
[
mImplementation
->
mCurrentRow
][
c
]
&
0xF0
)
|
(
mImplementation
->
mRowPointers
[
mImplementation
->
mCurrentRow
][
c
+
1
]
>>
4
);
#endif
}
}
else
{
//It is just an 8bit RGB or 8bit RGBA.
u32
widthBytes
=
std
::
min
(
mImplementation
->
mWidth
*
mImplementation
->
mBytesPerPixel
,
maxLength
);
for
(
u32
c
=
0
;
c
<
widthBytes
;
++
c
)
{
lineStart
[
c
]
=
mImplementation
->
mRowPointers
[
mImplementation
->
mCurrentRow
][
c
];
}
}
mImplementation
->
mCurrentRow
++
;
}
void
PNGLoader
::
CleanUp
()
{
#if PNG_LIBPNG_VER_MINOR < NEWER_INTERFACE_LIBPNG_VER_MINOR
png_destroy_read_struct
(
&
mImplementation
->
mPNGPtr
,
&
mImplementation
->
mInfoPtr
,
(
png_infopp
)
0
);
#else
// TODO: Add new interface code here
#endif
}
}
File Metadata
Details
Attached
Mime Type
text/x-c++
Expires
Wed, Jan 15, 11:12 PM (5 h, 24 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
72087
Default Alt Text
PNGLoader.cpp (10 KB)
Attached To
Mode
rEE Echo 3
Attached
Detach File
Event Timeline
Log In to Comment