From 7f0efe8017ce96406d9db8005047433b7d29396d Mon Sep 17 00:00:00 2001 From: castano Date: Mon, 2 Jul 2007 07:27:23 +0000 Subject: [PATCH] Add support for loading PSD files. Add missing files. --- trunk/src/nvimage/Image.cpp | 9 ++ trunk/src/nvimage/Image.h | 2 + trunk/src/nvimage/ImageIO.cpp | 149 +++++++++++++++++++++++++++++ trunk/src/nvimage/ImageIO.h | 2 + trunk/src/nvimage/PsdFile.h | 69 ++++++++++++++ trunk/src/nvmath/Basis.cpp | 173 ++++++++++++++++++++++++++++++++++ trunk/src/nvmath/Basis.h | 78 +++++++++++++++ 7 files changed, 482 insertions(+) create mode 100644 trunk/src/nvimage/PsdFile.h create mode 100644 trunk/src/nvmath/Basis.cpp create mode 100644 trunk/src/nvmath/Basis.h diff --git a/trunk/src/nvimage/Image.cpp b/trunk/src/nvimage/Image.cpp index dd193e0..f544adc 100644 --- a/trunk/src/nvimage/Image.cpp +++ b/trunk/src/nvimage/Image.cpp @@ -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; + } +} + diff --git a/trunk/src/nvimage/Image.h b/trunk/src/nvimage/Image.h index c1a1f9d..d5b0a1b 100644 --- a/trunk/src/nvimage/Image.h +++ b/trunk/src/nvimage/Image.h @@ -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(); diff --git a/trunk/src/nvimage/ImageIO.cpp b/trunk/src/nvimage/ImageIO.cpp index 8d4060b..3e6a9c0 100644 --- a/trunk/src/nvimage/ImageIO.cpp +++ b/trunk/src/nvimage/ImageIO.cpp @@ -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 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) diff --git a/trunk/src/nvimage/ImageIO.h b/trunk/src/nvimage/ImageIO.h index de3f380..7c3cde9 100644 --- a/trunk/src/nvimage/ImageIO.h +++ b/trunk/src/nvimage/ImageIO.h @@ -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); diff --git a/trunk/src/nvimage/PsdFile.h b/trunk/src/nvimage/PsdFile.h new file mode 100644 index 0000000..ecdecbc --- /dev/null +++ b/trunk/src/nvimage/PsdFile.h @@ -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 + +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 diff --git a/trunk/src/nvmath/Basis.cpp b/trunk/src/nvmath/Basis.cpp new file mode 100644 index 0000000..167cc38 --- /dev/null +++ b/trunk/src/nvmath/Basis.cpp @@ -0,0 +1,173 @@ +// This code is in the public domain -- castanyo@yahoo.es + +#include + +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)); +} +*/ + + diff --git a/trunk/src/nvmath/Basis.h b/trunk/src/nvmath/Basis.h new file mode 100644 index 0000000..90247be --- /dev/null +++ b/trunk/src/nvmath/Basis.h @@ -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 +#include +#include + +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