Add support for loading PSD files.
Add missing files.
This commit is contained in:
parent
b2252fc828
commit
68b0c2697c
@ -122,3 +122,12 @@ void Image::setFormat(Image::Format f)
|
||||
m_format = f;
|
||||
}
|
||||
|
||||
void Image::fill(Color32 c)
|
||||
{
|
||||
const uint size = m_width * m_height;
|
||||
for (uint i = 0; i < size; ++i)
|
||||
{
|
||||
m_data[i] = c;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -48,6 +48,8 @@ namespace nv
|
||||
NVIMAGE_API Format format() const;
|
||||
NVIMAGE_API void setFormat(Format f);
|
||||
|
||||
NVIMAGE_API void fill(Color32 c);
|
||||
|
||||
private:
|
||||
void free();
|
||||
|
||||
|
@ -11,6 +11,7 @@
|
||||
#include "Image.h"
|
||||
#include "FloatImage.h"
|
||||
#include "TgaFile.h"
|
||||
#include "PsdFile.h"
|
||||
|
||||
// Extern
|
||||
#if defined(HAVE_JPEG)
|
||||
@ -85,6 +86,7 @@ Image * nv::ImageIO::load(const char * name, Stream & s)
|
||||
Image * nv::ImageIO::loadTGA(Stream & s)
|
||||
{
|
||||
nvCheck(!s.isError());
|
||||
nvCheck(s.isLoading());
|
||||
|
||||
TgaHeader tga;
|
||||
s << tga;
|
||||
@ -315,6 +317,153 @@ bool nv::ImageIO::saveTGA(Stream & s, const Image * img)
|
||||
return true;
|
||||
}
|
||||
|
||||
/// Load PSD image.
|
||||
Image * nv::ImageIO::loadPSD(Stream & s)
|
||||
{
|
||||
nvCheck(!s.isError());
|
||||
nvCheck(s.isLoading());
|
||||
|
||||
PsdHeader header;
|
||||
s << header;
|
||||
|
||||
if (!header.isValid())
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!header.isSupported())
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int tmp;
|
||||
|
||||
// Skip mode data.
|
||||
s << tmp;
|
||||
s.seek(s.tell() + tmp);
|
||||
|
||||
// Skip image resources.
|
||||
s << tmp;
|
||||
s.seek(s.tell() + tmp);
|
||||
|
||||
// Skip the reserved data.
|
||||
s << tmp;
|
||||
s.seek(s.tell() + tmp);
|
||||
|
||||
// Find out if the data is compressed.
|
||||
// Known values:
|
||||
// 0: no compression
|
||||
// 1: RLE compressed
|
||||
uint16 compression;
|
||||
s << compression;
|
||||
|
||||
if (compression > 1) {
|
||||
// Unknown compression type.
|
||||
return NULL;
|
||||
}
|
||||
|
||||
uint channel_num = header.channel_count;
|
||||
|
||||
AutoPtr<Image> img(new Image());
|
||||
img->allocate(header.width, header.height);
|
||||
|
||||
if (channel_num < 4)
|
||||
{
|
||||
// Clear the image.
|
||||
img->fill(Color32(0, 0, 0, 0xFF));
|
||||
}
|
||||
else
|
||||
{
|
||||
// Enable alpha.
|
||||
img->setFormat(Image::Format_ARGB);
|
||||
|
||||
// Ignore remaining channels.
|
||||
channel_num = 4;
|
||||
}
|
||||
|
||||
|
||||
const uint pixel_count = header.height * header.width;
|
||||
|
||||
static const uint components[4] = {2, 1, 0, 3};
|
||||
|
||||
if (compression)
|
||||
{
|
||||
s.seek(s.tell() + header.height * header.channel_count * sizeof(uint16));
|
||||
|
||||
// Read RLE data.
|
||||
for (uint channel = 0; channel < channel_num; channel++)
|
||||
{
|
||||
uint8 * ptr = (uint8 *)img->pixels() + components[channel];
|
||||
|
||||
uint count = 0;
|
||||
while( count < pixel_count )
|
||||
{
|
||||
if (s.isAtEnd()) return NULL;
|
||||
|
||||
uint8 c;
|
||||
s << c;
|
||||
|
||||
uint len = c;
|
||||
if (len < 128)
|
||||
{
|
||||
// Copy next len+1 bytes literally.
|
||||
len++;
|
||||
count += len;
|
||||
if (count > pixel_count) return NULL;
|
||||
|
||||
while (len != 0)
|
||||
{
|
||||
s << *ptr;
|
||||
ptr += 4;
|
||||
len--;
|
||||
}
|
||||
}
|
||||
else if (len > 128)
|
||||
{
|
||||
// Next -len+1 bytes in the dest are replicated from next source byte.
|
||||
// (Interpret len as a negative 8-bit int.)
|
||||
len ^= 0xFF;
|
||||
len += 2;
|
||||
count += len;
|
||||
if (s.isAtEnd() || count > pixel_count) return NULL;
|
||||
|
||||
uint8 val;
|
||||
s << val;
|
||||
while( len != 0 ) {
|
||||
*ptr = val;
|
||||
ptr += 4;
|
||||
len--;
|
||||
}
|
||||
}
|
||||
else if( len == 128 ) {
|
||||
// No-op.
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// We're at the raw image data. It's each channel in order (Red, Green, Blue, Alpha, ...)
|
||||
// where each channel consists of an 8-bit value for each pixel in the image.
|
||||
|
||||
// Read the data by channel.
|
||||
for (uint channel = 0; channel < channel_num; channel++)
|
||||
{
|
||||
uint8 * ptr = (uint8 *)img->pixels() + components[channel];
|
||||
|
||||
// Read the data.
|
||||
uint count = pixel_count;
|
||||
while (count != 0)
|
||||
{
|
||||
s << *ptr;
|
||||
ptr += 4;
|
||||
count--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return img.release();
|
||||
}
|
||||
|
||||
#if defined(HAVE_PNG)
|
||||
|
||||
|
@ -19,6 +19,8 @@ namespace nv
|
||||
NVIMAGE_API Image * loadTGA(Stream & s);
|
||||
NVIMAGE_API bool saveTGA(Stream & s, const Image * img);
|
||||
|
||||
NVIMAGE_API Image * loadPSD(Stream & s);
|
||||
|
||||
#if defined(HAVE_PNG)
|
||||
NVIMAGE_API Image * loadPNG(Stream & s);
|
||||
NVIMAGE_API FloatImage * loadFloatPNG(Stream & s);
|
||||
|
69
src/nvimage/PsdFile.h
Normal file
69
src/nvimage/PsdFile.h
Normal file
@ -0,0 +1,69 @@
|
||||
// This code is in the public domain -- castanyo@yahoo.es
|
||||
|
||||
#ifndef NV_IMAGE_PSDFILE_H
|
||||
#define NV_IMAGE_PSDFILE_H
|
||||
|
||||
#include <nvcore/Stream.h>
|
||||
|
||||
namespace nv
|
||||
{
|
||||
enum PsdColorMode
|
||||
{
|
||||
PsdColorMode_Bitmap = 0,
|
||||
PsdColorMode_GrayScale = 1,
|
||||
PsdColorMode_Indexed = 2,
|
||||
PsdColorMode_RGB = 3,
|
||||
PsdColorMode_CMYK = 4,
|
||||
PsdColorMode_MultiChannel = 7,
|
||||
PsdColorMode_DuoTone = 8,
|
||||
PsdColorMode_LabColor = 9
|
||||
};
|
||||
|
||||
/// PSD header.
|
||||
struct PsdHeader
|
||||
{
|
||||
uint32 signature;
|
||||
uint16 version;
|
||||
uint8 reserved[6];
|
||||
uint16 channel_count;
|
||||
uint32 height;
|
||||
uint32 width;
|
||||
uint16 depth;
|
||||
uint16 color_mode;
|
||||
|
||||
bool isValid() const
|
||||
{
|
||||
return signature == 0x38425053; // '8BPS'
|
||||
}
|
||||
|
||||
bool isSupported() const
|
||||
{
|
||||
if (version != 1) {
|
||||
return false;
|
||||
}
|
||||
if (channel_count > 4) {
|
||||
return false;
|
||||
}
|
||||
if (depth != 8) {
|
||||
return false;
|
||||
}
|
||||
if (color_mode != PsdColorMode_RGB) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
inline Stream & operator<< (Stream & s, PsdHeader & head)
|
||||
{
|
||||
s << head.signature << head.version;
|
||||
for (int i = 0; i < 6; i++) {
|
||||
s << head.reserved[i];
|
||||
}
|
||||
return s << head.channel_count << head.height << head.width << head.depth << head.color_mode;
|
||||
}
|
||||
|
||||
} // nv namespace
|
||||
|
||||
#endif // NV_IMAGE_PSDFILE_H
|
173
src/nvmath/Basis.cpp
Normal file
173
src/nvmath/Basis.cpp
Normal file
@ -0,0 +1,173 @@
|
||||
// This code is in the public domain -- castanyo@yahoo.es
|
||||
|
||||
#include <nvmath/Basis.h>
|
||||
|
||||
using namespace nv;
|
||||
|
||||
|
||||
/// Normalize basis vectors.
|
||||
void Basis::normalize(float epsilon /*= NV_EPSILON*/)
|
||||
{
|
||||
normal = ::normalize(normal, epsilon);
|
||||
tangent = ::normalize(tangent, epsilon);
|
||||
bitangent = ::normalize(bitangent, epsilon);
|
||||
}
|
||||
|
||||
|
||||
/// Gram-Schmidt orthogonalization.
|
||||
/// @note Works only if the vectors are close to orthogonal.
|
||||
void Basis::orthonormalize(float epsilon /*= NV_EPSILON*/)
|
||||
{
|
||||
// N' = |N|
|
||||
// T' = |T - (N' dot T) N'|
|
||||
// B' = |B - (N' dot B) N' - (T' dot B) T'|
|
||||
|
||||
normal = ::normalize(normal, epsilon);
|
||||
|
||||
tangent -= normal * dot(normal, tangent);
|
||||
tangent = ::normalize(tangent, epsilon);
|
||||
|
||||
bitangent -= normal * dot(normal, bitangent);
|
||||
bitangent -= tangent * dot(tangent, bitangent);
|
||||
bitangent = ::normalize(bitangent, epsilon);
|
||||
}
|
||||
|
||||
|
||||
/// Robust orthonormalization.
|
||||
/// Returns an orthonormal basis even when the original is degenerate.
|
||||
void Basis::robustOrthonormalize(float epsilon /*= NV_EPSILON*/)
|
||||
{
|
||||
if (length(normal) < epsilon)
|
||||
{
|
||||
normal = cross(tangent, bitangent);
|
||||
|
||||
if (length(normal) < epsilon)
|
||||
{
|
||||
tangent = Vector3(1, 0, 0);
|
||||
bitangent = Vector3(0, 1, 0);
|
||||
normal = Vector3(0, 0, 1);
|
||||
return;
|
||||
}
|
||||
}
|
||||
normal = ::normalize(normal, epsilon);
|
||||
|
||||
tangent -= normal * dot(normal, tangent);
|
||||
bitangent -= normal * dot(normal, bitangent);
|
||||
|
||||
if (length(tangent) < epsilon)
|
||||
{
|
||||
if (length(bitangent) < epsilon)
|
||||
{
|
||||
buildFrameForDirection(normal);
|
||||
}
|
||||
else
|
||||
{
|
||||
tangent = cross(bitangent, normal);
|
||||
nvCheck(isNormalized(tangent, epsilon));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
tangent = ::normalize(tangent, epsilon);
|
||||
bitangent -= tangent * dot(tangent, bitangent);
|
||||
|
||||
if (length(bitangent) < epsilon)
|
||||
{
|
||||
bitangent = cross(tangent, normal);
|
||||
nvCheck(isNormalized(bitangent));
|
||||
}
|
||||
else
|
||||
{
|
||||
tangent = ::normalize(tangent, epsilon);
|
||||
}
|
||||
}
|
||||
|
||||
// Check vector lengths.
|
||||
nvCheck(isNormalized(normal, epsilon));
|
||||
nvCheck(isNormalized(tangent, epsilon));
|
||||
nvCheck(isNormalized(bitangent, epsilon));
|
||||
|
||||
// Check vector angles.
|
||||
nvCheck(equal(dot(normal, tangent), 0.0f, epsilon));
|
||||
nvCheck(equal(dot(normal, bitangent), 0.0f, epsilon));
|
||||
nvCheck(equal(dot(tangent, bitangent), 0.0f, epsilon));
|
||||
|
||||
// Check vector orientation.
|
||||
const float det = dot(cross(normal, tangent), bitangent);
|
||||
nvCheck(equal(det, 1.0f, epsilon) || equal(det, -1.0f, epsilon));
|
||||
}
|
||||
|
||||
|
||||
/// Build an arbitrary frame for the given direction.
|
||||
void Basis::buildFrameForDirection(Vector3::Arg d)
|
||||
{
|
||||
nvCheck(isNormalized(d));
|
||||
normal = d;
|
||||
|
||||
// Choose minimum axis.
|
||||
if (fabsf(normal.x()) < fabsf(normal.y()) && fabsf(normal.x()) < fabsf(normal.z()))
|
||||
{
|
||||
tangent = Vector3(1, 0, 0);
|
||||
}
|
||||
else if (fabsf(normal.y()) < fabsf(normal.z()))
|
||||
{
|
||||
tangent = Vector3(0, 1, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
tangent = Vector3(0, 0, 1);
|
||||
}
|
||||
|
||||
// Ortogonalize
|
||||
tangent -= normal * dot(normal, tangent);
|
||||
tangent = ::normalize(tangent);
|
||||
|
||||
bitangent = cross(normal, tangent);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
/// Transform by this basis. (From this basis to object space).
|
||||
Vector3 Basis::transform(Vector3::Arg v) const
|
||||
{
|
||||
Vector3 o = tangent * v.x();
|
||||
o += bitangent * v.y();
|
||||
o += normal * v.z();
|
||||
return o;
|
||||
}
|
||||
|
||||
/// Transform by the transpose. (From object space to this basis).
|
||||
Vector3 Basis::transformT(Vector3::Arg v)
|
||||
{
|
||||
return Vector3(dot(tangent, v), dot(bitangent, v), dot(normal, v));
|
||||
}
|
||||
|
||||
/// Transform by the inverse. (From object space to this basis).
|
||||
/// @note Uses Kramer's rule so the inverse is not accurate if the basis is ill-conditioned.
|
||||
Vector3 Basis::transformI(Vector3::Arg v) const
|
||||
{
|
||||
const float det = determinant();
|
||||
nvCheck(!equalf(det, 0.0f));
|
||||
|
||||
const float idet = 1.0f / det;
|
||||
|
||||
// Rows of the inverse matrix.
|
||||
Vector3 r0, r1, r2;
|
||||
r0.x = (bitangent.y() * normal.z() - bitangent.z() * normal.y()) * idet;
|
||||
r0.y = -(bitangent.x() * normal.z() - bitangent.z() * normal.x()) * idet;
|
||||
r0.z = (bitangent.x() * normal.y() - bitangent.y() * normal.x()) * idet;
|
||||
|
||||
r1.x = -(tangent.y() * normal.z() - tangent.z() * normal.y()) * idet;
|
||||
r1.y = (tangent.x() * normal.z() - tangent.z() * normal.x()) * idet;
|
||||
r1.z = -(tangent.x() * normal.y() - tangent.y() * normal.x()) * idet;
|
||||
|
||||
r2.x = (tangent.y() * bitangent.z() - tangent.z() * bitangent.y()) * idet;
|
||||
r2.y = -(tangent.x() * bitangent.z() - tangent.z() * bitangent.x()) * idet;
|
||||
r2.z = (tangent.x() * bitangent.y() - tangent.y() * bitangent.x()) * idet;
|
||||
|
||||
return Vector3(dot(v, r0), dot(v, r1), dot(v, r2));
|
||||
}
|
||||
*/
|
||||
|
||||
|
78
src/nvmath/Basis.h
Normal file
78
src/nvmath/Basis.h
Normal file
@ -0,0 +1,78 @@
|
||||
// This code is in the public domain -- castanyo@yahoo.es
|
||||
|
||||
#ifndef NV_MATH_BASIS_H
|
||||
#define NV_MATH_BASIS_H
|
||||
|
||||
#include <nvmath/nvmath.h>
|
||||
#include <nvmath/Vector.h>
|
||||
#include <nvmath/Matrix.h>
|
||||
|
||||
namespace nv
|
||||
{
|
||||
|
||||
/// Basis class to compute tangent space basis, ortogonalizations and to
|
||||
/// transform vectors from one space to another.
|
||||
struct Basis
|
||||
{
|
||||
/// Create a null basis.
|
||||
Basis() : tangent(0, 0, 0), bitangent(0, 0, 0), normal(0, 0, 0) {}
|
||||
|
||||
/// Create a basis given three vectors.
|
||||
Basis(Vector3::Arg n, Vector3::Arg t, Vector3::Arg b) : tangent(t), bitangent(b), normal(n) {}
|
||||
|
||||
/// Create a basis with the given tangent vectors and the handness.
|
||||
Basis(Vector3::Arg n, Vector3::Arg t, float sign)
|
||||
{
|
||||
build(n, t, sign);
|
||||
}
|
||||
|
||||
NVMATH_API void normalize(float epsilon = NV_EPSILON);
|
||||
NVMATH_API void orthonormalize(float epsilon = NV_EPSILON);
|
||||
NVMATH_API void robustOrthonormalize(float epsilon = NV_EPSILON);
|
||||
NVMATH_API void buildFrameForDirection(Vector3::Arg d);
|
||||
|
||||
/// Calculate the determinant [ F G N ] to obtain the handness of the basis.
|
||||
float handness() const
|
||||
{
|
||||
return determinant() > 0.0f ? 1.0f : -1.0f;
|
||||
}
|
||||
|
||||
/// Build a basis from 2 vectors and a handness flag.
|
||||
void build(Vector3::Arg n, Vector3::Arg t, float sign)
|
||||
{
|
||||
normal = n;
|
||||
tangent = t;
|
||||
bitangent = sign * cross(t, n);
|
||||
}
|
||||
|
||||
/// Compute the determinant of this basis.
|
||||
float determinant() const
|
||||
{
|
||||
return
|
||||
tangent.x() * bitangent.y() * normal.z() - tangent.z() * bitangent.y() * normal.x() +
|
||||
tangent.y() * bitangent.z() * normal.x() - tangent.y() * bitangent.x() * normal.z() +
|
||||
tangent.z() * bitangent.x() * normal.y() - tangent.x() * bitangent.z() * normal.y();
|
||||
}
|
||||
|
||||
/*
|
||||
// Get transform matrix for this basis.
|
||||
NVMATH_API Matrix matrix() const;
|
||||
|
||||
// Transform by this basis. (From this basis to object space).
|
||||
NVMATH_API Vector3 transform(Vector3::Arg v) const;
|
||||
|
||||
// Transform by the transpose. (From object space to this basis).
|
||||
NVMATH_API Vector3 transformT(Vector3::Arg v);
|
||||
|
||||
// Transform by the inverse. (From object space to this basis).
|
||||
NVMATH_API Vector3 transformI(Vector3::Arg v) const;
|
||||
*/
|
||||
|
||||
Vector3 tangent;
|
||||
Vector3 bitangent;
|
||||
Vector3 normal;
|
||||
};
|
||||
|
||||
} // nv namespace
|
||||
|
||||
#endif // NV_MATH_BASIS_H
|
Loading…
Reference in New Issue
Block a user