mirror of
https://github.com/TorqueGameEngines/Torque3D.git
synced 2026-02-10 15:01:07 +00:00
Merge branch 'development' of https://github.com/TorqueGameEngines/Torque3D into alpha41/scriptStability
This commit is contained in:
commit
e8210c8447
|
|
@ -82,6 +82,9 @@ endif()
|
|||
torqueAddSourceDirectories("gfx" "gfx/Null" "gfx/test" "gfx/bitmap" "gfx/bitmap/loaders"
|
||||
"gfx/util" "gfx/video" "gfx/sim" )
|
||||
|
||||
# add the stb headers
|
||||
set(TORQUE_INCLUDE_DIRECTORIES ${TORQUE_INCLUDE_DIRECTORIES} "gfx/bitmap/loaders/stb")
|
||||
|
||||
if (TORQUE_OPENGL)
|
||||
torqueAddSourceDirectories("gfx/gl" "gfx/gl/sdl" "gfx/gl/tGL")
|
||||
endif (TORQUE_OPENGL)
|
||||
|
|
|
|||
|
|
@ -156,7 +156,7 @@ bool Rigid::resolveCollision(const Point3F& p, const Point3F &normal, Rigid* rig
|
|||
return false;
|
||||
|
||||
// Compute impulse
|
||||
F32 d, n = -nv * (2.0f + restitution * rigid->restitution);
|
||||
F32 d, n = -nv * (1.0+(restitution + rigid->restitution)*0.5);
|
||||
Point3F a1,b1,c1;
|
||||
mCross(r1,normal,&a1);
|
||||
invWorldInertia.mulV(a1,&b1);
|
||||
|
|
|
|||
|
|
@ -900,9 +900,11 @@ bool RigidShape::onNewDataBlock(GameBaseData* dptr, bool reload)
|
|||
mRigid.restitution = mDataBlock->body.restitution;
|
||||
mRigid.setCenterOfMass(mDataBlock->massCenter);
|
||||
|
||||
// Ignores massBox, just set sphere for now. Derived objects
|
||||
// can set what they want.
|
||||
mRigid.setObjectInertia();
|
||||
// Set inertial tensor, default for the RigidShape is sphere
|
||||
if (mDataBlock->massBox.x > 0 && mDataBlock->massBox.y > 0 && mDataBlock->massBox.z > 0)
|
||||
mRigid.setObjectInertia(mDataBlock->massBox);
|
||||
else
|
||||
mRigid.setObjectInertia(mObjBox.maxExtents - mObjBox.minExtents);
|
||||
|
||||
scriptOnNewDataBlock();
|
||||
|
||||
|
|
@ -1115,9 +1117,9 @@ void RigidShape::updatePos(F32 dt)
|
|||
if (mCollisionList.getCount())
|
||||
{
|
||||
F32 k = mRigid.getKineticEnergy();
|
||||
F32 G = mNetGravity * dt;
|
||||
F32 G = mNetGravity;
|
||||
F32 Kg = 0.5 * mRigid.mass * G * G;
|
||||
if (k < sRestTol * Kg && ++restCount > sRestCount)
|
||||
if (k < sRestTol * Kg* dt && ++restCount > sRestCount)
|
||||
mRigid.setAtRest();
|
||||
}
|
||||
else
|
||||
|
|
@ -1205,8 +1207,12 @@ void RigidShape::updatePos(F32 dt)
|
|||
|
||||
void RigidShape::updateForces(F32 dt)
|
||||
{
|
||||
if (mDisableMove) return;
|
||||
|
||||
if (mDisableMove)
|
||||
{
|
||||
mRigid.linVelocity = Point3F::Zero;
|
||||
mRigid.angMomentum = Point3F::Zero;
|
||||
return;
|
||||
}
|
||||
Point3F torque(0, 0, 0);
|
||||
Point3F force(0, 0, mRigid.mass * mNetGravity);
|
||||
|
||||
|
|
@ -1222,10 +1228,6 @@ void RigidShape::updateForces(F32 dt)
|
|||
|
||||
mRigid.force = force;
|
||||
mRigid.torque = torque;
|
||||
|
||||
// If we're still atRest, make sure we're not accumulating anything
|
||||
if ((force.lenSquared() < mDataBlock->contactTol)&& (force.lenSquared() < mDataBlock->contactTol))
|
||||
mRigid.setAtRest();
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -1260,8 +1262,7 @@ bool RigidShape::updateCollision(F32 dt)
|
|||
}
|
||||
|
||||
// Resolve collisions
|
||||
bool collided = resolveCollision(mRigid,mCollisionList);
|
||||
resolveContacts(mRigid,mCollisionList,dt);
|
||||
bool collided = resolveCollision(mRigid,mCollisionList, dt);
|
||||
return collided;
|
||||
}
|
||||
|
||||
|
|
@ -1271,7 +1272,7 @@ bool RigidShape::updateCollision(F32 dt)
|
|||
Handle collision impacts, as opposed to contacts. Impulses are calculated based
|
||||
on standard collision resolution formulas.
|
||||
*/
|
||||
bool RigidShape::resolveCollision(Rigid& ns,CollisionList& cList)
|
||||
bool RigidShape::resolveCollision(Rigid& ns,CollisionList& cList, F32 dt)
|
||||
{
|
||||
PROFILE_SCOPE(RigidShape_resolveCollision);
|
||||
// Apply impulses to resolve collision
|
||||
|
|
@ -1279,6 +1280,11 @@ bool RigidShape::resolveCollision(Rigid& ns,CollisionList& cList)
|
|||
for (S32 i = 0; i < cList.getCount(); i++)
|
||||
{
|
||||
Collision& c = cList[i];
|
||||
if (c.object == this)
|
||||
{
|
||||
//Con::printf("IMPOSSIBLE!!!!--------------------------------> Self-collision event?");
|
||||
continue;
|
||||
}
|
||||
if (c.distance < mDataBlock->collisionTol)
|
||||
{
|
||||
// Velocity into surface
|
||||
|
|
@ -1288,9 +1294,7 @@ bool RigidShape::resolveCollision(Rigid& ns,CollisionList& cList)
|
|||
F32 vn = mDot(v, c.normal);
|
||||
|
||||
// Only interested in velocities greater than sContactTol,
|
||||
// velocities less than that will be dealt with as contacts
|
||||
// "constraints".
|
||||
if (vn < -mDataBlock->contactTol)
|
||||
if (mFabs(vn) > mDataBlock->contactTol)
|
||||
{
|
||||
|
||||
// Apply impulses to the rigid body to keep it from
|
||||
|
|
@ -1313,45 +1317,15 @@ bool RigidShape::resolveCollision(Rigid& ns,CollisionList& cList)
|
|||
queueCollision(col, v - col->getVelocity());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return collided;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
/** Resolve contact forces
|
||||
Resolve contact forces using the "penalty" method. Forces are generated based
|
||||
on the depth of penetration and the moment of inertia at the point of contact.
|
||||
*/
|
||||
bool RigidShape::resolveContacts(Rigid& ns,CollisionList& cList,F32 dt)
|
||||
{
|
||||
PROFILE_SCOPE(RigidShape_resolveContacts);
|
||||
// Use spring forces to manage contact constraints.
|
||||
bool collided = false;
|
||||
Point3F t,p(0,0,0),l(0,0,0);
|
||||
for (S32 i = 0; i < cList.getCount(); i++)
|
||||
{
|
||||
const Collision& c = cList[i];
|
||||
if (c.distance < mDataBlock->collisionTol)
|
||||
{
|
||||
|
||||
// Velocity into the surface
|
||||
Point3F v,r;
|
||||
ns.getOriginVector(c.point,&r);
|
||||
ns.getVelocity(r,&v);
|
||||
F32 vn = mDot(v,c.normal);
|
||||
|
||||
// Only interested in velocities less than mDataBlock->contactTol,
|
||||
// velocities greater than that are dealt with as collisions.
|
||||
if (mFabs(vn) < mDataBlock->contactTol)
|
||||
// velocities less than that will be dealt with as contacts
|
||||
// "constraints".
|
||||
else
|
||||
{
|
||||
collided = true;
|
||||
|
||||
Point3F t;
|
||||
// Penetration force. This is actually a spring which
|
||||
// will seperate the body from the collision surface.
|
||||
F32 zi = 2 * mFabs(mRigid.getZeroImpulse(r,c.normal));
|
||||
F32 s = (mDataBlock->collisionTol - c.distance) * zi - ((vn / mDataBlock->contactTol) * zi);
|
||||
F32 zi = 2 * mFabs(mRigid.getZeroImpulse(r, c.normal) / dt);
|
||||
F32 s = mMax((mDataBlock->collisionTol - c.distance) * zi - ((vn / 2.0) * zi),0.0f);
|
||||
Point3F f = c.normal * s;
|
||||
|
||||
// Friction impulse, calculated as a function of the
|
||||
|
|
@ -1359,10 +1333,10 @@ bool RigidShape::resolveContacts(Rigid& ns,CollisionList& cList,F32 dt)
|
|||
// perpendicular to the normal.
|
||||
Point3F uv = v - (c.normal * vn);
|
||||
F32 ul = uv.len();
|
||||
if (s > 0 && ul)
|
||||
if (s > 0 && ul)
|
||||
{
|
||||
uv /= -ul;
|
||||
F32 u = ul * ns.getZeroImpulse(r,uv);
|
||||
F32 u = ul * ns.getZeroImpulse(r, uv) / dt;
|
||||
s *= mRigid.friction;
|
||||
if (u > s)
|
||||
u = s;
|
||||
|
|
@ -1370,20 +1344,17 @@ bool RigidShape::resolveContacts(Rigid& ns,CollisionList& cList,F32 dt)
|
|||
}
|
||||
|
||||
// Accumulate forces
|
||||
p += f;
|
||||
mCross(r,f,&t);
|
||||
l += t;
|
||||
mCross(r, f, &t);
|
||||
ns.linMomentum += f * dt;
|
||||
ns.angMomentum += t * dt;
|
||||
ns.updateVelocity();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Contact constraint forces act over time...
|
||||
ns.linMomentum += p * dt;
|
||||
ns.angMomentum += l * dt;
|
||||
ns.updateVelocity();
|
||||
return true;
|
||||
}
|
||||
|
||||
return collided;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
|
|
@ -1693,6 +1664,8 @@ void RigidShape::initPersistFields()
|
|||
docsURL;
|
||||
addField("disableMove", TypeBool, Offset(mDisableMove, RigidShape),
|
||||
"When this flag is set, the vehicle will ignore throttle changes.");
|
||||
addField("isAtRest", TypeBool, Offset(mRigid.atRest, RigidShape),
|
||||
"Debug read of the rest state. do not set");
|
||||
Parent::initPersistFields();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -216,8 +216,7 @@ class RigidShape: public ShapeBase
|
|||
bool onNewDataBlock( GameBaseData *dptr, bool reload );
|
||||
void updatePos(F32 dt);
|
||||
bool updateCollision(F32 dt);
|
||||
bool resolveCollision(Rigid& ns,CollisionList& cList);
|
||||
bool resolveContacts(Rigid& ns,CollisionList& cList,F32 dt);
|
||||
bool resolveCollision(Rigid& ns,CollisionList& cList, F32 dt);
|
||||
bool resolveDisplacement(Rigid& ns,CollisionState *state,F32 dt);
|
||||
void checkTriggers();
|
||||
static void findCallback(SceneObject* obj,void * key);
|
||||
|
|
|
|||
|
|
@ -91,7 +91,7 @@ class SFXProfile;
|
|||
|
||||
typedef void* Light;
|
||||
|
||||
const F32 gGravity = -20;
|
||||
const F32 gGravity = -9.8f;
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
|
|
|
|||
|
|
@ -807,9 +807,9 @@ void Vehicle::updatePos(F32 dt)
|
|||
if (mCollisionList.getCount())
|
||||
{
|
||||
F32 k = mRigid.getKineticEnergy();
|
||||
F32 G = mNetGravity * dt;
|
||||
F32 G = mNetGravity;
|
||||
F32 Kg = 0.5 * mRigid.mass * G * G;
|
||||
if (k < sRestTol * Kg && ++restCount > sRestCount)
|
||||
if (k < sRestTol * Kg * dt && ++restCount > sRestCount)
|
||||
mRigid.setAtRest();
|
||||
}
|
||||
else
|
||||
|
|
|
|||
|
|
@ -40,6 +40,7 @@
|
|||
#include "core/fileObject.h"
|
||||
#include "persistence/taml/tamlCustom.h"
|
||||
#include "gui/editor/guiInspector.h"
|
||||
#include "console/script.h"
|
||||
|
||||
#include "sim/netObject.h"
|
||||
|
||||
|
|
@ -90,7 +91,7 @@ SimObject::SimObject()
|
|||
mNameSpace = NULL;
|
||||
mNotifyList = NULL;
|
||||
mFlags.set( ModStaticFields | ModDynamicFields );
|
||||
|
||||
mPrototype = true;
|
||||
mProgenitorFile = StringTable->EmptyString();
|
||||
|
||||
mFieldDictionary = NULL;
|
||||
|
|
@ -159,7 +160,8 @@ void SimObject::initPersistFields()
|
|||
|
||||
addProtectedField("inheritFrom", TypeString, Offset(mInheritFrom, SimObject), &setInheritFrom, &defaultProtectedGetFn,
|
||||
"Optional Name of object to inherit from as a parent.");
|
||||
|
||||
|
||||
addProtectedField("Prototype", TypeBool, Offset(mPrototype, SimObject), &_doPrototype, &defaultProtectedGetFn, "Prototype Methods", AbstractClassRep::FieldFlags::FIELD_ComponentInspectors);
|
||||
endGroup( "Ungrouped" );
|
||||
|
||||
addGroup( "Object" );
|
||||
|
|
@ -212,6 +214,15 @@ void SimObject::initPersistFields()
|
|||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
bool SimObject::_doPrototype(void* object, const char* index, const char* data)
|
||||
{
|
||||
if (!Con::isFunction("PrototypeClass")) return false;
|
||||
if (dAtoi(data) != 1) return false;
|
||||
SimObject* obj = reinterpret_cast<SimObject*>(object);
|
||||
String command = String("PrototypeClass(") + (obj->getName()? String(obj->getName()) : String::ToString(obj->getId())) + ");";
|
||||
Con::evaluate(command.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
String SimObject::describeSelf() const
|
||||
{
|
||||
|
|
@ -2707,6 +2718,59 @@ DefineEngineMethod(SimObject, getMethodSigs, ArrayObject*, (bool commands), (fal
|
|||
|
||||
return dictionary;
|
||||
}
|
||||
|
||||
DefineEngineFunction(getMethodSigsNS, ArrayObject*, (StringTableEntry className, bool commands), (false),
|
||||
"List the methods defined on this object.\n\n"
|
||||
"Each description is a newline-separated vector with the following elements:\n"
|
||||
"- method prototype string.\n"
|
||||
"- Documentation string (not including prototype). This takes up the remainder of the vector.\n"
|
||||
"@return An ArrayObject populated with (name,description) pairs of all methods defined on the object.")
|
||||
{
|
||||
|
||||
Namespace* ns = Con::lookupNamespace(className);
|
||||
if (!ns)
|
||||
return 0;
|
||||
|
||||
ArrayObject* dictionary = new ArrayObject();
|
||||
dictionary->registerObject();
|
||||
|
||||
VectorPtr<Namespace::Entry*> vec(__FILE__, __LINE__);
|
||||
ns->getEntryList(&vec);
|
||||
for (Vector< Namespace::Entry* >::iterator j = vec.begin(); j != vec.end(); j++)
|
||||
{
|
||||
Namespace::Entry* e = *j;
|
||||
|
||||
if (commands)
|
||||
{
|
||||
if ((e->mType < Namespace::Entry::ConsoleFunctionType))
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((e->mType > Namespace::Entry::ScriptCallbackType))
|
||||
continue;
|
||||
}
|
||||
StringBuilder str;
|
||||
str.append("function ");
|
||||
str.append(ns->getName());
|
||||
str.append("::");
|
||||
str.append(e->getPrototypeSig());
|
||||
str.append('\n');
|
||||
str.append("{");
|
||||
String docs = e->getDocString();
|
||||
if (!docs.isEmpty())
|
||||
{
|
||||
str.append("\n/*");
|
||||
str.append(docs);
|
||||
str.append("\n*/");
|
||||
}
|
||||
str.append('\n');
|
||||
str.append("}");
|
||||
dictionary->push_back(e->mFunctionName, str.end());
|
||||
}
|
||||
|
||||
return dictionary;
|
||||
}
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
namespace {
|
||||
|
|
@ -3262,6 +3326,33 @@ DefineEngineMethod( SimObject, getFieldCount, S32, (),,
|
|||
return list.size() - numDummyEntries;
|
||||
}
|
||||
|
||||
DefineEngineFunction(getFieldCountNS, S32, (StringTableEntry className), ,
|
||||
"Get the number of static fields on the name space.\n"
|
||||
"@return The number of static fields defined on the object.")
|
||||
{
|
||||
Namespace* ns = Con::lookupNamespace(className);
|
||||
if (!ns)
|
||||
return 0;
|
||||
AbstractClassRep* rep = ns->mClassRep;
|
||||
if (!rep)
|
||||
return 0;
|
||||
|
||||
const AbstractClassRep::FieldList& list = rep->mFieldList;
|
||||
const AbstractClassRep::Field* f;
|
||||
U32 numDummyEntries = 0;
|
||||
|
||||
for (S32 i = 0; i < list.size(); i++)
|
||||
{
|
||||
f = &list[i];
|
||||
|
||||
// The special field types do not need to be counted.
|
||||
if (f->type >= AbstractClassRep::ARCFirstCustomField || f->flag.test(AbstractClassRep::FieldFlags::FIELD_ComponentInspectors))
|
||||
numDummyEntries++;
|
||||
}
|
||||
|
||||
return list.size() - numDummyEntries;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
DefineEngineMethod( SimObject, getField, const char*, ( S32 index ),,
|
||||
|
|
@ -3293,6 +3384,42 @@ DefineEngineMethod( SimObject, getField, const char*, ( S32 index ),,
|
|||
return "";
|
||||
}
|
||||
|
||||
DefineEngineFunction(getFieldNS, const char*, (StringTableEntry className,S32 index), ,
|
||||
"Retrieve the value of a static field by index.\n"
|
||||
"@param index The index of the static field.\n"
|
||||
"@return The value of the static field with the given index or \"\".")
|
||||
{
|
||||
Namespace* ns = Con::lookupNamespace(className);
|
||||
if (!ns)
|
||||
return 0;
|
||||
AbstractClassRep* rep = ns->mClassRep;
|
||||
if (!rep)
|
||||
return 0;
|
||||
|
||||
const AbstractClassRep::FieldList& list = rep->mFieldList;
|
||||
if ((index < 0) || (index >= list.size()))
|
||||
return "";
|
||||
|
||||
const AbstractClassRep::Field* f;
|
||||
S32 currentField = 0;
|
||||
for (U32 i = 0; i < list.size() && currentField <= index; i++)
|
||||
{
|
||||
f = &list[i];
|
||||
|
||||
// The special field types can be skipped.
|
||||
if (f->type >= AbstractClassRep::ARCFirstCustomField || f->flag.test(AbstractClassRep::FieldFlags::FIELD_ComponentInspectors))
|
||||
continue;
|
||||
|
||||
if (currentField == index)
|
||||
return f->pFieldname;
|
||||
|
||||
currentField++;
|
||||
}
|
||||
|
||||
// if we found nada, return nada.
|
||||
return "";
|
||||
}
|
||||
|
||||
DefineEngineFunction(getClassHierarchy, const char*, (const char* name), ,
|
||||
"Returns the inheritance hierarchy for a given class.")
|
||||
{
|
||||
|
|
|
|||
|
|
@ -300,7 +300,7 @@ class SimObject: public ConsoleObject, public TamlCallbacks
|
|||
SimObject* nextIdObject;
|
||||
|
||||
StringTableEntry mInheritFrom;
|
||||
|
||||
bool mPrototype;
|
||||
/// SimGroup we're contained in, if any.
|
||||
SimGroup* mGroup;
|
||||
|
||||
|
|
@ -388,7 +388,7 @@ class SimObject: public ConsoleObject, public TamlCallbacks
|
|||
public:
|
||||
inline void setProgenitorFile(const char* pFile) { mProgenitorFile = StringTable->insert(pFile); }
|
||||
inline StringTableEntry getProgenitorFile(void) const { return mProgenitorFile; }
|
||||
|
||||
static bool _doPrototype(void* object, const char* index, const char* data);
|
||||
protected:
|
||||
/// Taml callbacks.
|
||||
virtual void onTamlPreWrite(void) {}
|
||||
|
|
|
|||
|
|
@ -565,15 +565,10 @@ void DDSFile::SurfaceData::dumpImage(DDSFile *dds, U32 mip, const char *file)
|
|||
|
||||
// Copy our data in.
|
||||
dMemcpy(foo->getWritableBits(), mMips[mip], dds->getSurfaceSize(dds->mHeight, dds->mWidth, mip) );
|
||||
|
||||
FileStream stream;
|
||||
|
||||
stream.open( file, Torque::FS::File::Write );
|
||||
|
||||
if ( stream.getStatus() == Stream::Ok )
|
||||
if(!foo->writeBitmap("png", file))
|
||||
{
|
||||
// Write it out.
|
||||
foo->writeBitmap("png", stream);
|
||||
Con::errorf("DDSFile::SurfaceData::dumpImage() - Error writing %s !", file);
|
||||
}
|
||||
|
||||
// Clean up.
|
||||
|
|
|
|||
|
|
@ -1192,7 +1192,7 @@ bool GBitmap::write(Stream& io_rStream) const
|
|||
//-------------------------------------- Persistent I/O
|
||||
//
|
||||
|
||||
bool GBitmap::readBitmap( const String &bmType, Stream &ioStream )
|
||||
bool GBitmap::readBitmap(const String& bmType, const Torque::Path& path)
|
||||
{
|
||||
PROFILE_SCOPE(ResourceGBitmap_readBitmap);
|
||||
const GBitmap::Registration *regInfo = GBitmap::sFindRegInfo( bmType );
|
||||
|
|
@ -1203,11 +1203,22 @@ bool GBitmap::readBitmap( const String &bmType, Stream &ioStream )
|
|||
return false;
|
||||
}
|
||||
|
||||
return regInfo->readFunc( ioStream, this );
|
||||
return regInfo->readFunc(path, this);
|
||||
}
|
||||
|
||||
bool GBitmap::writeBitmap( const String &bmType, Stream &ioStream, U32 compressionLevel )
|
||||
bool GBitmap::writeBitmap( const String &bmType, const Torque::Path& path, U32 compressionLevel )
|
||||
{
|
||||
FileStream stream;
|
||||
if (!stream.open(path, Torque::FS::File::Write))
|
||||
{
|
||||
Con::errorf("GBitmap::writeBitmap failed to open path %s", path.getFullFileName().c_str());
|
||||
stream.close();
|
||||
return false;
|
||||
}
|
||||
|
||||
// free file for stb
|
||||
stream.close();
|
||||
|
||||
const GBitmap::Registration *regInfo = GBitmap::sFindRegInfo( bmType );
|
||||
|
||||
if ( regInfo == NULL )
|
||||
|
|
@ -1216,7 +1227,7 @@ bool GBitmap::writeBitmap( const String &bmType, Stream &ioStream, U32 compress
|
|||
return false;
|
||||
}
|
||||
|
||||
return regInfo->writeFunc( this, ioStream, (compressionLevel == U32_MAX) ? regInfo->defaultCompression : compressionLevel );
|
||||
return regInfo->writeFunc(path, this, (compressionLevel == U32_MAX) ? regInfo->defaultCompression : compressionLevel );
|
||||
}
|
||||
|
||||
template<> void *Resource<GBitmap>::create(const Torque::Path &path)
|
||||
|
|
@ -1239,7 +1250,7 @@ template<> void *Resource<GBitmap>::create(const Torque::Path &path)
|
|||
|
||||
GBitmap *bmp = new GBitmap;
|
||||
const String extension = path.getExtension();
|
||||
if( !bmp->readBitmap( extension, stream ) )
|
||||
if( !bmp->readBitmap( extension, path ) )
|
||||
{
|
||||
Con::errorf( "Resource<GBitmap>::create - error reading '%s'", path.getFullPath().c_str() );
|
||||
delete bmp;
|
||||
|
|
@ -1431,21 +1442,14 @@ DefineEngineFunction(saveScaledImage, bool, (const char* bitmapSource, const cha
|
|||
Torque::Path destinationPath = Torque::Path(bitmapDest);
|
||||
destinationPath.setExtension("png");
|
||||
|
||||
// Open up the file on disk.
|
||||
FileStream fs;
|
||||
if (!fs.open(destinationPath.getFullPath(), Torque::FS::File::Write))
|
||||
if(!image->writeBitmap("png", destinationPath.getFullPath()))
|
||||
{
|
||||
Con::errorf("saveScaledImage() - Failed to open output file '%s'!", bitmapDest);
|
||||
Con::errorf("saveScaledImage() - Error writing %s !", bitmapDest);
|
||||
delete image;
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
image->writeBitmap("png", fs);
|
||||
|
||||
fs.close();
|
||||
delete image;
|
||||
}
|
||||
|
||||
|
||||
delete image;
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -71,10 +71,10 @@ public:
|
|||
struct Registration
|
||||
{
|
||||
/// The read function prototype.
|
||||
typedef bool(*ReadFunc)(Stream &stream, GBitmap *bitmap);
|
||||
typedef bool(*ReadFunc)(const Torque::Path& path, GBitmap* bitmap);
|
||||
|
||||
/// The write function prototype. Compression levels are image-specific - see their registration declaration for details.
|
||||
typedef bool(*WriteFunc)(GBitmap *bitmap, Stream &stream, U32 compressionLevel);
|
||||
typedef bool(*WriteFunc)(const Torque::Path& path, GBitmap* bitmap, U32 compressionLevel);
|
||||
|
||||
/// Used to sort the registrations so that
|
||||
/// lookups occur in a fixed order.
|
||||
|
|
@ -241,13 +241,16 @@ public:
|
|||
/// Read a bitmap from a stream
|
||||
/// @param bmType This is a file extension to describe the type of the data [i.e. "png" for PNG file, etc]
|
||||
/// @param ioStream The stream to read from
|
||||
bool readBitmap( const String &bmType, Stream &ioStream );
|
||||
bool readBitmap(const String& bmType, const Torque::Path& path);
|
||||
|
||||
/// Write a bitmap to a stream
|
||||
/// @param bmType This is a file extension to describe the type of the data [i.e. "png" for PNG file, etc]
|
||||
/// @param ioStream The stream to read from
|
||||
/// @param compressionLevel Image format-specific compression level. If set to U32_MAX, we use the default compression defined when the format was registered.
|
||||
bool writeBitmap( const String &bmType, Stream &ioStream, U32 compressionLevel = U32_MAX );
|
||||
/// @param compressionLevel Image format specific compression level. For JPEG sets the quality level percentage, range 0 to 100.
|
||||
/// For PNG compression level is 0 - 10
|
||||
/// Not used for other image formats.
|
||||
|
||||
bool writeBitmap( const String &bmType, const Torque::Path& path, U32 compressionLevel = U32_MAX );
|
||||
|
||||
bool readMNG(Stream& io_rStream); // located in bitmapMng.cc
|
||||
bool writeMNG(Stream& io_rStream) const;
|
||||
|
|
|
|||
|
|
@ -1,246 +0,0 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "core/stream/stream.h"
|
||||
|
||||
#include "gfx/bitmap/gBitmap.h"
|
||||
|
||||
|
||||
static bool sReadBMP(Stream &stream, GBitmap *bitmap);
|
||||
static bool sWriteBMP(GBitmap *bitmap, Stream &stream, U32 compressionLevel);
|
||||
|
||||
static struct _privateRegisterBMP
|
||||
{
|
||||
_privateRegisterBMP()
|
||||
{
|
||||
GBitmap::Registration reg;
|
||||
|
||||
reg.extensions.push_back( "bmp" );
|
||||
|
||||
reg.readFunc = sReadBMP;
|
||||
reg.writeFunc = sWriteBMP;
|
||||
|
||||
GBitmap::sRegisterFormat( reg );
|
||||
}
|
||||
} sStaticRegisterBMP;
|
||||
|
||||
|
||||
// structures mirror those defined by the win32 API
|
||||
|
||||
struct RGBQUAD
|
||||
{
|
||||
U8 rgbBlue;
|
||||
U8 rgbGreen;
|
||||
U8 rgbRed;
|
||||
U8 rgbReserved;
|
||||
};
|
||||
|
||||
struct BITMAPFILEHEADER
|
||||
{
|
||||
U16 bfType;
|
||||
U32 bfSize;
|
||||
U16 bfReserved1;
|
||||
U16 bfReserved2;
|
||||
U32 bfOffBits;
|
||||
};
|
||||
|
||||
struct BITMAPINFOHEADER
|
||||
{
|
||||
U32 biSize;
|
||||
S32 biWidth;
|
||||
S32 biHeight;
|
||||
U16 biPlanes;
|
||||
U16 biBitCount;
|
||||
U32 biCompression;
|
||||
U32 biSizeImage;
|
||||
S32 biXPelsPerMeter;
|
||||
S32 biYPelsPerMeter;
|
||||
U32 biClrUsed;
|
||||
U32 biClrImportant;
|
||||
};
|
||||
|
||||
// constants for the biCompression field
|
||||
#define BI_RGB 0L
|
||||
#define BI_RLE8 1L
|
||||
#define BI_RLE4 2L
|
||||
#define BI_BITFIELDS 3L
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
//-------------------------------------- Supplementary I/O (Partially located in
|
||||
// bitmapPng.cc)
|
||||
//
|
||||
|
||||
static bool sReadBMP(Stream &stream, GBitmap *bitmap)
|
||||
{
|
||||
PROFILE_SCOPE(sReadBMP);
|
||||
BITMAPINFOHEADER bi;
|
||||
BITMAPFILEHEADER bf;
|
||||
RGBQUAD rgb[256];
|
||||
|
||||
stream.read(&bf.bfType);
|
||||
stream.read(&bf.bfSize);
|
||||
stream.read(&bf.bfReserved1);
|
||||
stream.read(&bf.bfReserved2);
|
||||
stream.read(&bf.bfOffBits);
|
||||
|
||||
stream.read(&bi.biSize);
|
||||
stream.read(&bi.biWidth);
|
||||
stream.read(&bi.biHeight);
|
||||
stream.read(&bi.biPlanes);
|
||||
stream.read(&bi.biBitCount);
|
||||
stream.read(&bi.biCompression);
|
||||
stream.read(&bi.biSizeImage);
|
||||
stream.read(&bi.biXPelsPerMeter);
|
||||
stream.read(&bi.biYPelsPerMeter);
|
||||
stream.read(&bi.biClrUsed);
|
||||
stream.read(&bi.biClrImportant);
|
||||
|
||||
GFXFormat fmt = GFXFormatR8G8B8;
|
||||
if(bi.biBitCount == 8)
|
||||
{
|
||||
// read in texture palette
|
||||
if(!bi.biClrUsed)
|
||||
bi.biClrUsed = 256;
|
||||
stream.read(sizeof(RGBQUAD) * bi.biClrUsed, rgb);
|
||||
}
|
||||
bitmap->allocateBitmap(bi.biWidth, bi.biHeight, false, fmt);
|
||||
U32 width = bitmap->getWidth();
|
||||
U32 height = bitmap->getHeight();
|
||||
U32 bytesPerPixel = bitmap->getBytesPerPixel();
|
||||
|
||||
for(U32 i = 0; i < bi.biHeight; i++)
|
||||
{
|
||||
U8 *rowDest = bitmap->getAddress(0, height - i - 1);
|
||||
if (bi.biBitCount == 8)
|
||||
{
|
||||
// use palette...don't worry about being slow
|
||||
for (S32 j=0; j<width; j++)
|
||||
{
|
||||
U8 palIdx;
|
||||
stream.read(&palIdx);
|
||||
U8 * pixelLocation = &rowDest[j*bytesPerPixel];
|
||||
pixelLocation[0] = rgb[palIdx].rgbRed;
|
||||
pixelLocation[1] = rgb[palIdx].rgbGreen;
|
||||
pixelLocation[2] = rgb[palIdx].rgbBlue;
|
||||
if (bytesPerPixel==3)
|
||||
pixelLocation[3] = 255;
|
||||
}
|
||||
}
|
||||
else
|
||||
stream.read(bytesPerPixel * width, rowDest);
|
||||
}
|
||||
|
||||
if(bytesPerPixel == 3 && bi.biBitCount != 8) // do BGR swap
|
||||
{
|
||||
U8 *ptr = bitmap->getAddress(0,0);
|
||||
for(S32 i = 0; i < width * height; i++)
|
||||
{
|
||||
U8 tmp = ptr[0];
|
||||
ptr[0] = ptr[2];
|
||||
ptr[2] = tmp;
|
||||
ptr += 3;
|
||||
}
|
||||
}
|
||||
|
||||
// We know BMP's don't have any transparency
|
||||
bitmap->setHasTransparency(false);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool sWriteBMP(GBitmap *bitmap, Stream &stream, U32 compressionLevel)
|
||||
{
|
||||
TORQUE_UNUSED( compressionLevel ); // BMP does not use compression
|
||||
|
||||
BITMAPINFOHEADER bi;
|
||||
BITMAPFILEHEADER bf;
|
||||
|
||||
bi.biSize = sizeof(BITMAPINFOHEADER);
|
||||
bi.biWidth = bitmap->getWidth();
|
||||
bi.biHeight = bitmap->getHeight(); //our data is top-down
|
||||
bi.biPlanes = 1;
|
||||
|
||||
if(bitmap->getFormat() == GFXFormatR8G8B8)
|
||||
{
|
||||
bi.biBitCount = 24;
|
||||
bi.biCompression = BI_RGB;
|
||||
bi.biClrUsed = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
bi.biBitCount = 0;
|
||||
bi.biCompression = BI_RGB; // Removes warning C4701 on line
|
||||
AssertISV(false, "GBitmap::writeMSBmp - only support R8G8B8 formats!");
|
||||
}
|
||||
|
||||
U32 width = bitmap->getWidth();
|
||||
U32 height = bitmap->getHeight();
|
||||
|
||||
U32 bytesPP = bi.biBitCount >> 3;
|
||||
bi.biSizeImage = width * height * bytesPP;
|
||||
bi.biXPelsPerMeter = 0;
|
||||
bi.biYPelsPerMeter = 0;
|
||||
bi.biClrUsed = 0;
|
||||
bi.biClrImportant = 0;
|
||||
|
||||
bf.bfType = makeFourCCTag('B','M',0,0); //Type of file 'BM'
|
||||
bf.bfOffBits= sizeof(BITMAPINFOHEADER)
|
||||
+ sizeof(BITMAPFILEHEADER)
|
||||
+ (sizeof(RGBQUAD)*bi.biClrUsed);
|
||||
bf.bfSize = bf.bfOffBits + bi.biSizeImage;
|
||||
bf.bfReserved1 = 0;
|
||||
bf.bfReserved2 = 0;
|
||||
|
||||
stream.write(bf.bfType);
|
||||
stream.write(bf.bfSize);
|
||||
stream.write(bf.bfReserved1);
|
||||
stream.write(bf.bfReserved2);
|
||||
stream.write(bf.bfOffBits);
|
||||
|
||||
stream.write(bi.biSize);
|
||||
stream.write(bi.biWidth);
|
||||
stream.write(bi.biHeight);
|
||||
stream.write(bi.biPlanes);
|
||||
stream.write(bi.biBitCount);
|
||||
stream.write(bi.biCompression);
|
||||
stream.write(bi.biSizeImage);
|
||||
stream.write(bi.biXPelsPerMeter);
|
||||
stream.write(bi.biYPelsPerMeter);
|
||||
stream.write(bi.biClrUsed);
|
||||
stream.write(bi.biClrImportant);
|
||||
|
||||
//write the bitmap bits
|
||||
U8* pMSUpsideDownBits = new U8[bi.biSizeImage];
|
||||
for (U32 i = 0; i < height; i++)
|
||||
{
|
||||
const U8* pSrc = bitmap->getAddress(0, i);
|
||||
U8* pDst = pMSUpsideDownBits + (height - i - 1) * width * bytesPP;
|
||||
|
||||
dMemcpy(pDst, pSrc, width * bytesPP);
|
||||
}
|
||||
|
||||
stream.write(bi.biSizeImage, pMSUpsideDownBits);
|
||||
delete [] pMSUpsideDownBits;
|
||||
|
||||
return stream.getStatus() == Stream::Ok;
|
||||
}
|
||||
|
|
@ -1,251 +0,0 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "ljpeg/jpeglib.h"
|
||||
|
||||
#include "core/stream/stream.h"
|
||||
|
||||
#include "gfx/bitmap/gBitmap.h"
|
||||
|
||||
|
||||
static bool sReadJPG(Stream &stream, GBitmap *bitmap);
|
||||
static bool sWriteJPG(GBitmap *bitmap, Stream &stream, U32 compressionLevel);
|
||||
|
||||
static struct _privateRegisterJPG
|
||||
{
|
||||
_privateRegisterJPG()
|
||||
{
|
||||
GBitmap::Registration reg;
|
||||
|
||||
reg.priority = 50;
|
||||
reg.extensions.push_back( "jpeg" );
|
||||
reg.extensions.push_back( "jpg" );
|
||||
|
||||
reg.readFunc = sReadJPG;
|
||||
reg.writeFunc = sWriteJPG;
|
||||
|
||||
GBitmap::sRegisterFormat( reg );
|
||||
}
|
||||
} sStaticRegisterJPG;
|
||||
|
||||
//-------------------------------------- Replacement I/O for standard LIBjpeg
|
||||
// functions. we don't wanna use
|
||||
// FILE*'s...
|
||||
static S32 jpegReadDataFn(void *client_data, U8 *data, S32 length)
|
||||
{
|
||||
Stream *stream = (Stream*)client_data;
|
||||
AssertFatal(stream != NULL, "jpegReadDataFn::No stream.");
|
||||
S32 pos = stream->getPosition();
|
||||
if (stream->read(length, data))
|
||||
return length;
|
||||
|
||||
if (stream->getStatus() == Stream::EOS)
|
||||
return (stream->getPosition()-pos);
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
//--------------------------------------
|
||||
static S32 jpegWriteDataFn(void *client_data, U8 *data, S32 length)
|
||||
{
|
||||
Stream *stream = (Stream*)client_data;
|
||||
AssertFatal(stream != NULL, "jpegWriteDataFn::No stream.");
|
||||
if (stream->write(length, data))
|
||||
return length;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
//--------------------------------------
|
||||
static S32 jpegFlushDataFn(void *)
|
||||
{
|
||||
// do nothing since we can't flush the stream object
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
//--------------------------------------
|
||||
static S32 jpegErrorFn(void *client_data)
|
||||
{
|
||||
Stream *stream = (Stream*)client_data;
|
||||
AssertFatal(stream != NULL, "jpegErrorFn::No stream.");
|
||||
return (stream->getStatus() != Stream::Ok);
|
||||
}
|
||||
|
||||
|
||||
//--------------------------------------
|
||||
static bool sReadJPG(Stream &stream, GBitmap *bitmap)
|
||||
{
|
||||
PROFILE_SCOPE(sReadJPG);
|
||||
JFREAD = jpegReadDataFn;
|
||||
JFERROR = jpegErrorFn;
|
||||
|
||||
jpeg_decompress_struct cinfo;
|
||||
jpeg_error_mgr jerr;
|
||||
|
||||
// We set up the normal JPEG error routines, then override error_exit.
|
||||
//cinfo.err = jpeg_std_error(&jerr.pub);
|
||||
//jerr.pub.error_exit = my_error_exit;
|
||||
|
||||
// if (setjmp(jerr.setjmp_buffer))
|
||||
// {
|
||||
// // If we get here, the JPEG code has signaled an error.
|
||||
// // We need to clean up the JPEG object, close the input file, and return.
|
||||
// jpeg_destroy_decompress(&cinfo);
|
||||
// return false;
|
||||
// }
|
||||
|
||||
|
||||
cinfo.err = jpeg_std_error(&jerr); // set up the normal JPEG error routines.
|
||||
cinfo.client_data = (void*)&stream; // set the stream into the client_data
|
||||
|
||||
// Now we can initialize the JPEG decompression object.
|
||||
jpeg_create_decompress(&cinfo);
|
||||
|
||||
jpeg_stdio_src(&cinfo);
|
||||
|
||||
// Read file header, set default decompression parameters
|
||||
jpeg_read_header(&cinfo, true);
|
||||
|
||||
GFXFormat format;
|
||||
switch (cinfo.out_color_space)
|
||||
{
|
||||
case JCS_GRAYSCALE: format = GFXFormatA8; break;
|
||||
case JCS_RGB: format = GFXFormatR8G8B8; break;
|
||||
default:
|
||||
jpeg_destroy_decompress(&cinfo);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Start decompressor
|
||||
jpeg_start_decompress(&cinfo);
|
||||
|
||||
// allocate the bitmap space and init internal variables...
|
||||
bitmap->allocateBitmap(cinfo.output_width, cinfo.output_height, false, format);
|
||||
|
||||
// Set up the row pointers...
|
||||
U32 rowBytes = cinfo.output_width * cinfo.output_components;
|
||||
|
||||
U8* pBase = (U8*)bitmap->getBits();
|
||||
for (U32 i = 0; i < bitmap->getHeight(); i++)
|
||||
{
|
||||
JSAMPROW rowPointer = pBase + (U64)(i * rowBytes);
|
||||
jpeg_read_scanlines(&cinfo, &rowPointer, 1);
|
||||
}
|
||||
|
||||
// Finish decompression
|
||||
jpeg_finish_decompress(&cinfo);
|
||||
|
||||
// Release JPEG decompression object
|
||||
// This is an important step since it will release a good deal of memory.
|
||||
jpeg_destroy_decompress(&cinfo);
|
||||
|
||||
// We know JPEG's don't have any transparency
|
||||
bitmap->setHasTransparency(false);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
static bool sWriteJPG(GBitmap *bitmap, Stream &stream, U32 compressionLevel)
|
||||
{
|
||||
TORQUE_UNUSED(compressionLevel); // compression level not currently hooked up
|
||||
|
||||
GFXFormat format = bitmap->getFormat();
|
||||
|
||||
// JPEG format does not support transparency so any image
|
||||
// in Alpha format should be saved as a grayscale which coincides
|
||||
// with how the readJPEG function will read-in a JPEG. So the
|
||||
// only formats supported are RGB and Alpha, not RGBA.
|
||||
AssertFatal(format == GFXFormatR8G8B8 || format == GFXFormatA8,
|
||||
"GBitmap::writeJPEG: ONLY RGB bitmap writing supported at this time.");
|
||||
if (format != GFXFormatR8G8B8 && format != GFXFormatA8)
|
||||
return false;
|
||||
|
||||
// maximum image size allowed
|
||||
#define MAX_HEIGHT 4096
|
||||
if (bitmap->getHeight() > MAX_HEIGHT)
|
||||
return false;
|
||||
|
||||
// Bind our own stream writing, error, and memory flush functions
|
||||
// to the jpeg library interface
|
||||
JFWRITE = jpegWriteDataFn;
|
||||
JFFLUSH = jpegFlushDataFn;
|
||||
JFERROR = jpegErrorFn;
|
||||
|
||||
// Allocate and initialize our jpeg compression structure and error manager
|
||||
jpeg_compress_struct cinfo;
|
||||
jpeg_error_mgr jerr;
|
||||
|
||||
cinfo.err = jpeg_std_error(&jerr); // set up the normal JPEG error routines.
|
||||
cinfo.client_data = (void*)&stream; // set the stream into the client_data
|
||||
jpeg_create_compress(&cinfo); // allocates a small amount of memory
|
||||
|
||||
// specify the destination for the compressed data(our stream)
|
||||
jpeg_stdio_dest(&cinfo);
|
||||
|
||||
// set the image properties
|
||||
cinfo.image_width = bitmap->getWidth(); // image width
|
||||
cinfo.image_height = bitmap->getHeight(); // image height
|
||||
cinfo.input_components = bitmap->getBytesPerPixel(); // samples per pixel(RGB:3, Alpha:1)
|
||||
|
||||
switch (format)
|
||||
{
|
||||
case GFXFormatA8: // no alpha support in JPEG format, so turn it into a grayscale
|
||||
cinfo.in_color_space = JCS_GRAYSCALE;
|
||||
break;
|
||||
case GFXFormatR8G8B8: // otherwise we are writing in RGB format
|
||||
cinfo.in_color_space = JCS_RGB;
|
||||
break;
|
||||
default:
|
||||
AssertFatal( false, "Format not handled in GBitmap::writeJPEG() switch" );
|
||||
break;
|
||||
}
|
||||
// use default compression params(75% compression)
|
||||
jpeg_set_defaults(&cinfo);
|
||||
|
||||
// begin JPEG compression cycle
|
||||
jpeg_start_compress(&cinfo, true);
|
||||
|
||||
// Set up the row pointers...
|
||||
U32 rowBytes = cinfo.image_width * cinfo.input_components;
|
||||
|
||||
U8* pBase = (U8*)bitmap->getBits();
|
||||
for (U32 i = 0; i < bitmap->getHeight(); i++)
|
||||
{
|
||||
// write the image data
|
||||
JSAMPROW rowPointer = pBase + (i * rowBytes);
|
||||
jpeg_write_scanlines(&cinfo, &rowPointer, 1);
|
||||
}
|
||||
|
||||
// complete the compression cycle
|
||||
jpeg_finish_compress(&cinfo);
|
||||
|
||||
// release the JPEG compression object
|
||||
jpeg_destroy_compress(&cinfo);
|
||||
|
||||
// return success
|
||||
return true;
|
||||
}
|
||||
|
|
@ -1,645 +0,0 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "platform/platform.h"
|
||||
|
||||
#include "core/stream/fileStream.h"
|
||||
#include "core/stream/memStream.h"
|
||||
#include "core/strings/stringFunctions.h"
|
||||
|
||||
#include "gfx/bitmap/gBitmap.h"
|
||||
#include "gfx/bitmap/pngUtils.h"
|
||||
|
||||
#define PNG_INTERNAL 1
|
||||
#include <time.h>
|
||||
#include "lpng/png.h"
|
||||
#include "zlib/zlib.h"
|
||||
|
||||
#ifdef NULL
|
||||
#undef NULL
|
||||
#define NULL 0
|
||||
#endif
|
||||
|
||||
|
||||
static bool sReadPNG(Stream &stream, GBitmap *bitmap);
|
||||
|
||||
/// Compression levels for PNGs range from 0-9.
|
||||
/// A value outside that range will cause the write routine to look for the best compression for a given PNG. This can be slow.
|
||||
static bool sWritePNG(GBitmap *bitmap, Stream &stream, U32 compressionLevel);
|
||||
static bool _writePNG(GBitmap *bitmap, Stream &stream, U32 compressionLevel, U32 strategy, U32 filter);
|
||||
|
||||
static struct _privateRegisterPNG
|
||||
{
|
||||
_privateRegisterPNG()
|
||||
{
|
||||
GBitmap::Registration reg;
|
||||
|
||||
reg.priority = 100;
|
||||
reg.extensions.push_back( "png" );
|
||||
|
||||
reg.readFunc = sReadPNG;
|
||||
reg.writeFunc = sWritePNG;
|
||||
reg.defaultCompression = 6;
|
||||
|
||||
GBitmap::sRegisterFormat( reg );
|
||||
}
|
||||
} sStaticRegisterPNG;
|
||||
|
||||
|
||||
//-------------------------------------- Replacement I/O for standard LIBPng
|
||||
// functions. we don't wanna use
|
||||
// FILE*'s...
|
||||
static void pngReadDataFn(png_structp png_ptr,
|
||||
png_bytep data,
|
||||
png_size_t length)
|
||||
{
|
||||
AssertFatal(png_get_io_ptr(png_ptr) != NULL, "No stream?");
|
||||
|
||||
Stream *strm = (Stream*)png_get_io_ptr(png_ptr);
|
||||
bool success = strm->read((U32)length, data);
|
||||
AssertFatal(success, "pngReadDataFn - failed to read from stream!");
|
||||
}
|
||||
|
||||
|
||||
//--------------------------------------
|
||||
static void pngWriteDataFn(png_structp png_ptr,
|
||||
png_bytep data,
|
||||
png_size_t length)
|
||||
{
|
||||
AssertFatal(png_get_io_ptr(png_ptr) != NULL, "No stream?");
|
||||
|
||||
Stream *strm = (Stream*)png_get_io_ptr(png_ptr);
|
||||
bool success = strm->write((U32)length, data);
|
||||
AssertFatal(success, "pngWriteDataFn - failed to write to stream!");
|
||||
}
|
||||
|
||||
|
||||
//--------------------------------------
|
||||
static void pngFlushDataFn(png_structp /*png_ptr*/)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
static png_voidp pngMallocFn(png_structp /*png_ptr*/, png_size_t size)
|
||||
{
|
||||
return FrameAllocator::alloc((U32)size);
|
||||
}
|
||||
|
||||
static void pngFreeFn(png_structp /*png_ptr*/, png_voidp /*mem*/)
|
||||
{
|
||||
}
|
||||
|
||||
static png_voidp pngRealMallocFn(png_structp /*png_ptr*/, png_size_t size)
|
||||
{
|
||||
return (png_voidp)dMalloc(size);
|
||||
}
|
||||
|
||||
static void pngRealFreeFn(png_structp /*png_ptr*/, png_voidp mem)
|
||||
{
|
||||
dFree(mem);
|
||||
}
|
||||
|
||||
//--------------------------------------
|
||||
static void pngFatalErrorFn(png_structp /*png_ptr*/,
|
||||
png_const_charp pMessage)
|
||||
{
|
||||
AssertISV(false, avar("Error reading PNG file:\n %s", pMessage));
|
||||
}
|
||||
|
||||
|
||||
//--------------------------------------
|
||||
static void pngWarningFn(png_structp, png_const_charp /*pMessage*/)
|
||||
{
|
||||
// AssertWarn(false, avar("Warning reading PNG file:\n %s", pMessage));
|
||||
}
|
||||
|
||||
|
||||
//--------------------------------------
|
||||
static bool sReadPNG(Stream &stream, GBitmap *bitmap)
|
||||
{
|
||||
PROFILE_SCOPE(sReadPNG);
|
||||
static const U32 cs_headerBytesChecked = 8;
|
||||
|
||||
U8 header[cs_headerBytesChecked];
|
||||
stream.read(cs_headerBytesChecked, header);
|
||||
|
||||
bool isPng = png_check_sig(header, cs_headerBytesChecked) != 0;
|
||||
if (!isPng)
|
||||
{
|
||||
AssertWarn(false, "GBitmap::readPNG: stream doesn't contain a PNG");
|
||||
return false;
|
||||
}
|
||||
|
||||
U32 prevWaterMark = FrameAllocator::getWaterMark();
|
||||
png_structp png_ptr = png_create_read_struct_2(PNG_LIBPNG_VER_STRING,
|
||||
NULL,
|
||||
pngFatalErrorFn,
|
||||
pngWarningFn,
|
||||
NULL,
|
||||
pngRealMallocFn,
|
||||
pngRealFreeFn);
|
||||
|
||||
if (png_ptr == NULL)
|
||||
{
|
||||
FrameAllocator::setWaterMark(prevWaterMark);
|
||||
return false;
|
||||
}
|
||||
|
||||
png_infop info_ptr = png_create_info_struct(png_ptr);
|
||||
if (info_ptr == NULL)
|
||||
{
|
||||
png_destroy_read_struct(&png_ptr,
|
||||
(png_infopp)NULL,
|
||||
(png_infopp)NULL);
|
||||
|
||||
FrameAllocator::setWaterMark(prevWaterMark);
|
||||
return false;
|
||||
}
|
||||
|
||||
png_infop end_info = png_create_info_struct(png_ptr);
|
||||
if (end_info == NULL)
|
||||
{
|
||||
png_destroy_read_struct(&png_ptr,
|
||||
&info_ptr,
|
||||
(png_infopp)NULL);
|
||||
|
||||
FrameAllocator::setWaterMark(prevWaterMark);
|
||||
return false;
|
||||
}
|
||||
|
||||
png_set_read_fn(png_ptr, &stream, pngReadDataFn);
|
||||
|
||||
// Read off the info on the image.
|
||||
png_set_sig_bytes(png_ptr, cs_headerBytesChecked);
|
||||
png_read_info(png_ptr, info_ptr);
|
||||
|
||||
// OK, at this point, if we have reached it ok, then we can reset the
|
||||
// image to accept the new data...
|
||||
//
|
||||
bitmap->deleteImage();
|
||||
|
||||
png_uint_32 width;
|
||||
png_uint_32 height;
|
||||
S32 bit_depth;
|
||||
S32 color_type;
|
||||
|
||||
png_get_IHDR(png_ptr, info_ptr,
|
||||
&width, &height, // obv.
|
||||
&bit_depth, &color_type, // obv.
|
||||
NULL, // interlace
|
||||
NULL, // compression_type
|
||||
NULL); // filter_type
|
||||
|
||||
// First, handle the color transformations. We need this to read in the
|
||||
// data as RGB or RGBA, _always_, with a maximal channel width of 8 bits.
|
||||
//
|
||||
bool transAlpha = false;
|
||||
GFXFormat format = GFXFormatR8G8B8;
|
||||
|
||||
// Strip off any 16 bit info
|
||||
//
|
||||
if (bit_depth == 16 && color_type != PNG_COLOR_TYPE_GRAY)
|
||||
{
|
||||
png_set_strip_16(png_ptr);
|
||||
}
|
||||
|
||||
// Expand a transparency channel into a full alpha channel...
|
||||
//
|
||||
if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS))
|
||||
{
|
||||
png_set_expand(png_ptr);
|
||||
transAlpha = true;
|
||||
}
|
||||
|
||||
if (color_type == PNG_COLOR_TYPE_PALETTE)
|
||||
{
|
||||
png_set_expand(png_ptr);
|
||||
format = transAlpha ? GFXFormatR8G8B8A8 : GFXFormatR8G8B8;
|
||||
}
|
||||
else if (color_type == PNG_COLOR_TYPE_GRAY)
|
||||
{
|
||||
png_set_expand(png_ptr);
|
||||
|
||||
if (bit_depth == 16)
|
||||
format = GFXFormatL16;
|
||||
else
|
||||
format = GFXFormatA8;
|
||||
}
|
||||
else if (color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
|
||||
{
|
||||
png_set_expand(png_ptr);
|
||||
png_set_gray_to_rgb(png_ptr);
|
||||
format = GFXFormatR8G8B8A8;
|
||||
}
|
||||
else if (color_type == PNG_COLOR_TYPE_RGB)
|
||||
{
|
||||
format = transAlpha ? GFXFormatR8G8B8A8 : GFXFormatR8G8B8;
|
||||
png_set_expand(png_ptr);
|
||||
}
|
||||
else if (color_type == PNG_COLOR_TYPE_RGB_ALPHA)
|
||||
{
|
||||
png_set_expand(png_ptr);
|
||||
format = GFXFormatR8G8B8A8;
|
||||
}
|
||||
|
||||
// Update the info pointer with the result of the transformations
|
||||
// above...
|
||||
png_read_update_info(png_ptr, info_ptr);
|
||||
|
||||
png_uint_32 rowBytes = (png_uint_32)png_get_rowbytes(png_ptr, info_ptr);
|
||||
if (format == GFXFormatR8G8B8)
|
||||
{
|
||||
AssertFatal(rowBytes == width * 3,
|
||||
"Error, our rowbytes are incorrect for this transform... (3)");
|
||||
}
|
||||
else if (format == GFXFormatR8G8B8A8)
|
||||
{
|
||||
AssertFatal(rowBytes == width * 4,
|
||||
"Error, our rowbytes are incorrect for this transform... (4)");
|
||||
}
|
||||
else if (format == GFXFormatL16)
|
||||
{
|
||||
AssertFatal(rowBytes == width * 2,
|
||||
"Error, our rowbytes are incorrect for this transform... (2)");
|
||||
}
|
||||
|
||||
// actually allocate the bitmap space...
|
||||
bitmap->allocateBitmap(width, height,
|
||||
false, // don't extrude miplevels...
|
||||
format); // use determined format...
|
||||
|
||||
// Set up the row pointers...
|
||||
png_bytep* rowPointers = new png_bytep[ height ];
|
||||
U8* pBase = (U8*)bitmap->getBits();
|
||||
|
||||
for (U32 i = 0; i < height; i++)
|
||||
rowPointers[i] = pBase + (i * rowBytes);
|
||||
|
||||
// And actually read the image!
|
||||
png_read_image(png_ptr, rowPointers);
|
||||
|
||||
// We're outta here, destroy the png structs, and release the lock
|
||||
// as quickly as possible...
|
||||
//png_read_end(png_ptr, end_info);
|
||||
delete [] rowPointers;
|
||||
png_read_end(png_ptr, NULL);
|
||||
png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
|
||||
|
||||
// Ok, the image is read in, now we need to finish up the initialization,
|
||||
// which means: setting up the detailing members, init'ing the palette
|
||||
// key, etc...
|
||||
//
|
||||
// actually, all of that was handled by allocateBitmap, so we're outta here
|
||||
//
|
||||
|
||||
// Check this bitmap for transparency
|
||||
bitmap->checkForTransparency();
|
||||
|
||||
FrameAllocator::setWaterMark(prevWaterMark);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
static bool _writePNG(GBitmap *bitmap, Stream &stream, U32 compressionLevel, U32 strategy, U32 filter)
|
||||
{
|
||||
GFXFormat format = bitmap->getFormat();
|
||||
|
||||
// ONLY RGB bitmap writing supported at this time!
|
||||
AssertFatal( format == GFXFormatR8G8B8 ||
|
||||
format == GFXFormatR8G8B8A8 ||
|
||||
format == GFXFormatR8G8B8X8 ||
|
||||
format == GFXFormatA8 ||
|
||||
format == GFXFormatR5G6B5 ||
|
||||
format == GFXFormatR8G8B8A8_LINEAR_FORCE, "_writePNG: ONLY RGB bitmap writing supported at this time.");
|
||||
|
||||
if ( format != GFXFormatR8G8B8 &&
|
||||
format != GFXFormatR8G8B8A8 &&
|
||||
format != GFXFormatR8G8B8X8 &&
|
||||
format != GFXFormatA8 &&
|
||||
format != GFXFormatR5G6B5 && format != GFXFormatR8G8B8A8_LINEAR_FORCE)
|
||||
return false;
|
||||
|
||||
png_structp png_ptr = png_create_write_struct_2(PNG_LIBPNG_VER_STRING,
|
||||
NULL,
|
||||
pngFatalErrorFn,
|
||||
pngWarningFn,
|
||||
NULL,
|
||||
pngMallocFn,
|
||||
pngFreeFn);
|
||||
if (png_ptr == NULL)
|
||||
return (false);
|
||||
|
||||
png_infop info_ptr = png_create_info_struct(png_ptr);
|
||||
if (info_ptr == NULL)
|
||||
{
|
||||
png_destroy_write_struct(&png_ptr, (png_infopp)NULL);
|
||||
return false;
|
||||
}
|
||||
|
||||
png_set_write_fn(png_ptr, &stream, pngWriteDataFn, pngFlushDataFn);
|
||||
|
||||
// Set the compression level and image filters
|
||||
png_set_compression_window_bits(png_ptr, 15);
|
||||
png_set_compression_level(png_ptr, compressionLevel);
|
||||
png_set_filter(png_ptr, 0, filter);
|
||||
|
||||
// Set the image information here. Width and height are up to 2^31,
|
||||
// bit_depth is one of 1, 2, 4, 8, or 16, but valid values also depend on
|
||||
// the color_type selected. color_type is one of PNG_COLOR_TYPE_GRAY,
|
||||
// PNG_COLOR_TYPE_GRAY_ALPHA, PNG_COLOR_TYPE_PALETTE, PNG_COLOR_TYPE_RGB,
|
||||
// or PNG_COLOR_TYPE_RGB_ALPHA. interlace is either PNG_INTERLACE_NONE or
|
||||
// PNG_INTERLACE_ADAM7, and the compression_type and filter_type MUST
|
||||
// currently be PNG_COMPRESSION_TYPE_BASE and PNG_FILTER_TYPE_BASE. REQUIRED
|
||||
|
||||
U32 width = bitmap->getWidth();
|
||||
U32 height = bitmap->getHeight();
|
||||
|
||||
if (format == GFXFormatR8G8B8)
|
||||
{
|
||||
png_set_IHDR(png_ptr, info_ptr,
|
||||
width, height, // the width & height
|
||||
8, PNG_COLOR_TYPE_RGB, // bit_depth, color_type,
|
||||
NULL, // no interlace
|
||||
NULL, // compression type
|
||||
NULL); // filter type
|
||||
}
|
||||
else if (format == GFXFormatR8G8B8A8 || format == GFXFormatR8G8B8X8 || format == GFXFormatR8G8B8A8_LINEAR_FORCE)
|
||||
{
|
||||
png_set_IHDR(png_ptr, info_ptr,
|
||||
width, height, // the width & height
|
||||
8, PNG_COLOR_TYPE_RGB_ALPHA, // bit_depth, color_type,
|
||||
NULL, // no interlace
|
||||
NULL, // compression type
|
||||
NULL); // filter type
|
||||
}
|
||||
else if (format == GFXFormatA8)
|
||||
{
|
||||
png_set_IHDR(png_ptr, info_ptr,
|
||||
width, height, // the width & height
|
||||
8, PNG_COLOR_TYPE_GRAY, // bit_depth, color_type,
|
||||
NULL, // no interlace
|
||||
NULL, // compression type
|
||||
NULL); // filter type
|
||||
}
|
||||
else if (format == GFXFormatR5G6B5)
|
||||
{
|
||||
png_set_IHDR(png_ptr, info_ptr,
|
||||
width, height, // the width & height
|
||||
16, PNG_COLOR_TYPE_GRAY, // bit_depth, color_type,
|
||||
PNG_INTERLACE_NONE, // no interlace
|
||||
PNG_COMPRESSION_TYPE_DEFAULT, // compression type
|
||||
PNG_FILTER_TYPE_DEFAULT); // filter type
|
||||
|
||||
png_color_8_struct sigBit = { 0 };
|
||||
sigBit.gray = 16;
|
||||
png_set_sBIT(png_ptr, info_ptr, &sigBit );
|
||||
|
||||
png_set_swap( png_ptr );
|
||||
}
|
||||
|
||||
png_write_info(png_ptr, info_ptr);
|
||||
FrameAllocatorMarker marker;
|
||||
png_bytep* row_pointers = (png_bytep*)marker.alloc( height * sizeof( png_bytep ) );
|
||||
for (U32 i=0; i<height; i++)
|
||||
row_pointers[i] = const_cast<png_bytep>(bitmap->getAddress(0, i));
|
||||
|
||||
png_write_image(png_ptr, row_pointers);
|
||||
|
||||
// Write S3TC data if present...
|
||||
// Write FXT1 data if present...
|
||||
|
||||
png_write_end(png_ptr, info_ptr);
|
||||
png_destroy_write_struct(&png_ptr, (png_infopp)NULL);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
static bool sWritePNG(GBitmap *bitmap, Stream &stream, U32 compressionLevel)
|
||||
{
|
||||
U32 waterMark = FrameAllocator::getWaterMark();
|
||||
|
||||
if ( compressionLevel < 10 )
|
||||
{
|
||||
bool retVal = _writePNG(bitmap, stream, compressionLevel, 0, PNG_ALL_FILTERS);
|
||||
FrameAllocator::setWaterMark(waterMark);
|
||||
return retVal;
|
||||
}
|
||||
|
||||
// check all our methods of compression to find the best one and use it
|
||||
U8* buffer = new U8[1 << 22]; // 4 Megs. Should be enough...
|
||||
MemStream* pMemStream = new MemStream(1 << 22, buffer, false, true);
|
||||
|
||||
const U32 zStrategies[] = { Z_DEFAULT_STRATEGY,
|
||||
Z_FILTERED };
|
||||
const U32 pngFilters[] = { PNG_FILTER_NONE,
|
||||
PNG_FILTER_SUB,
|
||||
PNG_FILTER_UP,
|
||||
PNG_FILTER_AVG,
|
||||
PNG_FILTER_PAETH,
|
||||
PNG_ALL_FILTERS };
|
||||
|
||||
U32 minSize = 0xFFFFFFFF;
|
||||
U32 bestStrategy = 0xFFFFFFFF;
|
||||
U32 bestFilter = 0xFFFFFFFF;
|
||||
U32 bestCLevel = 0xFFFFFFFF;
|
||||
|
||||
for (U32 cl = 0; cl <=9; cl++)
|
||||
{
|
||||
for (U32 zs = 0; zs < 2; zs++)
|
||||
{
|
||||
for (U32 pf = 0; pf < 6; pf++)
|
||||
{
|
||||
pMemStream->setPosition(0);
|
||||
|
||||
U32 waterMarkInner = FrameAllocator::getWaterMark();
|
||||
|
||||
if (_writePNG(bitmap, *pMemStream, cl, zStrategies[zs], pngFilters[pf]) == false)
|
||||
AssertFatal(false, "Handle this error!");
|
||||
|
||||
FrameAllocator::setWaterMark(waterMarkInner);
|
||||
|
||||
if (pMemStream->getPosition() < minSize)
|
||||
{
|
||||
minSize = pMemStream->getPosition();
|
||||
bestStrategy = zs;
|
||||
bestFilter = pf;
|
||||
bestCLevel = cl;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
AssertFatal(minSize != 0xFFFFFFFF, "Error, no best found?");
|
||||
|
||||
delete pMemStream;
|
||||
delete [] buffer;
|
||||
|
||||
|
||||
bool retVal = _writePNG(bitmap, stream,
|
||||
bestCLevel,
|
||||
zStrategies[bestStrategy],
|
||||
pngFilters[bestFilter]);
|
||||
FrameAllocator::setWaterMark(waterMark);
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Stores PNG stream data
|
||||
struct DeferredPNGWriterData {
|
||||
png_structp png_ptr;
|
||||
png_infop info_ptr;
|
||||
U32 width;
|
||||
U32 height;
|
||||
};
|
||||
DeferredPNGWriter::DeferredPNGWriter() :
|
||||
mData( NULL ),
|
||||
mActive(false)
|
||||
{
|
||||
mData = new DeferredPNGWriterData();
|
||||
}
|
||||
DeferredPNGWriter::~DeferredPNGWriter()
|
||||
{
|
||||
delete mData;
|
||||
}
|
||||
|
||||
bool DeferredPNGWriter::begin( GFXFormat format, S32 width, S32 height, Stream &stream, U32 compressionLevel )
|
||||
{
|
||||
// ONLY RGB bitmap writing supported at this time!
|
||||
AssertFatal( format == GFXFormatR8G8B8 ||
|
||||
format == GFXFormatR8G8B8A8 ||
|
||||
format == GFXFormatR8G8B8X8 ||
|
||||
format == GFXFormatA8 ||
|
||||
format == GFXFormatR5G6B5, "_writePNG: ONLY RGB bitmap writing supported at this time.");
|
||||
|
||||
if ( format != GFXFormatR8G8B8 &&
|
||||
format != GFXFormatR8G8B8A8 &&
|
||||
format != GFXFormatR8G8B8X8 &&
|
||||
format != GFXFormatA8 &&
|
||||
format != GFXFormatR5G6B5 )
|
||||
return false;
|
||||
|
||||
mData->png_ptr = png_create_write_struct_2(PNG_LIBPNG_VER_STRING,
|
||||
NULL,
|
||||
pngFatalErrorFn,
|
||||
pngWarningFn,
|
||||
NULL,
|
||||
pngRealMallocFn,
|
||||
pngRealFreeFn);
|
||||
if (mData->png_ptr == NULL)
|
||||
return (false);
|
||||
|
||||
mData->info_ptr = png_create_info_struct(mData->png_ptr);
|
||||
if (mData->info_ptr == NULL)
|
||||
{
|
||||
png_destroy_write_struct(&mData->png_ptr, (png_infopp)NULL);
|
||||
return false;
|
||||
}
|
||||
|
||||
png_set_write_fn(mData->png_ptr, &stream, pngWriteDataFn, pngFlushDataFn);
|
||||
|
||||
// Set the compression level and image filters
|
||||
png_set_compression_window_bits(mData->png_ptr, 15);
|
||||
png_set_compression_level(mData->png_ptr, compressionLevel);
|
||||
png_set_filter(mData->png_ptr, 0, PNG_ALL_FILTERS);
|
||||
|
||||
// Set the image information here. Width and height are up to 2^31,
|
||||
// bit_depth is one of 1, 2, 4, 8, or 16, but valid values also depend on
|
||||
// the color_type selected. color_type is one of PNG_COLOR_TYPE_GRAY,
|
||||
// PNG_COLOR_TYPE_GRAY_ALPHA, PNG_COLOR_TYPE_PALETTE, PNG_COLOR_TYPE_RGB,
|
||||
// or PNG_COLOR_TYPE_RGB_ALPHA. interlace is either PNG_INTERLACE_NONE or
|
||||
// PNG_INTERLACE_ADAM7, and the compression_type and filter_type MUST
|
||||
// currently be PNG_COMPRESSION_TYPE_BASE and PNG_FILTER_TYPE_BASE. REQUIRED
|
||||
|
||||
if (format == GFXFormatR8G8B8)
|
||||
{
|
||||
png_set_IHDR(mData->png_ptr, mData->info_ptr,
|
||||
width, height, // the width & height
|
||||
8, PNG_COLOR_TYPE_RGB, // bit_depth, color_type,
|
||||
NULL, // no interlace
|
||||
NULL, // compression type
|
||||
NULL); // filter type
|
||||
}
|
||||
else if (format == GFXFormatR8G8B8A8 || format == GFXFormatR8G8B8X8)
|
||||
{
|
||||
png_set_IHDR(mData->png_ptr, mData->info_ptr,
|
||||
width, height, // the width & height
|
||||
8, PNG_COLOR_TYPE_RGB_ALPHA, // bit_depth, color_type,
|
||||
NULL, // no interlace
|
||||
NULL, // compression type
|
||||
NULL); // filter type
|
||||
}
|
||||
else if (format == GFXFormatA8)
|
||||
{
|
||||
png_set_IHDR(mData->png_ptr, mData->info_ptr,
|
||||
width, height, // the width & height
|
||||
8, PNG_COLOR_TYPE_GRAY, // bit_depth, color_type,
|
||||
NULL, // no interlace
|
||||
NULL, // compression type
|
||||
NULL); // filter type
|
||||
}
|
||||
else if (format == GFXFormatR5G6B5)
|
||||
{
|
||||
png_set_IHDR(mData->png_ptr, mData->info_ptr,
|
||||
width, height, // the width & height
|
||||
16, PNG_COLOR_TYPE_GRAY, // bit_depth, color_type,
|
||||
PNG_INTERLACE_NONE, // no interlace
|
||||
PNG_COMPRESSION_TYPE_DEFAULT, // compression type
|
||||
PNG_FILTER_TYPE_DEFAULT); // filter type
|
||||
|
||||
png_color_8_struct sigBit = { 0 };
|
||||
sigBit.gray = 16;
|
||||
png_set_sBIT(mData->png_ptr, mData->info_ptr, &sigBit );
|
||||
|
||||
png_set_swap( mData->png_ptr );
|
||||
}
|
||||
|
||||
png_write_info(mData->png_ptr, mData->info_ptr);
|
||||
|
||||
mActive = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
void DeferredPNGWriter::append( GBitmap* bitmap, U32 rows)
|
||||
{
|
||||
AssertFatal(mActive, "Cannot append to an inactive DeferredPNGWriter!");
|
||||
|
||||
U32 height = getMin( bitmap->getHeight(), rows);
|
||||
|
||||
FrameAllocatorMarker marker;
|
||||
png_bytep* row_pointers = (png_bytep*)marker.alloc( height * sizeof( png_bytep ) );
|
||||
for (U32 i=0; i<height; i++)
|
||||
row_pointers[i] = const_cast<png_bytep>(bitmap->getAddress(0, i));
|
||||
|
||||
png_write_rows(mData->png_ptr, row_pointers, height);
|
||||
}
|
||||
void DeferredPNGWriter::end()
|
||||
{
|
||||
AssertFatal(mActive, "Cannot end an inactive DeferredPNGWriter!");
|
||||
|
||||
png_write_end(mData->png_ptr, mData->info_ptr);
|
||||
png_destroy_write_struct(&mData->png_ptr, (png_infopp)NULL);
|
||||
|
||||
mActive = false;
|
||||
}
|
||||
271
Engine/source/gfx/bitmap/loaders/bitmapSTB.cpp
Normal file
271
Engine/source/gfx/bitmap/loaders/bitmapSTB.cpp
Normal file
|
|
@ -0,0 +1,271 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "platform/platform.h"
|
||||
|
||||
#include "core/stream/fileStream.h"
|
||||
#include "core/stream/memStream.h"
|
||||
#include "core/strings/stringFunctions.h"
|
||||
#include "gfx/bitmap/gBitmap.h"
|
||||
|
||||
#ifdef __clang__
|
||||
#define STBIWDEF static inline
|
||||
#endif
|
||||
|
||||
#define STB_IMAGE_IMPLEMENTATION
|
||||
#define STB_IMAGE_STATIC
|
||||
#include "stb_image.h"
|
||||
|
||||
#define STB_IMAGE_WRITE_IMPLEMENTATION
|
||||
#define STB_IMAGE_WRITE_STATIC
|
||||
#include "stb_image_write.h"
|
||||
|
||||
static bool sReadSTB(const Torque::Path& path, GBitmap* bitmap);
|
||||
static bool sWriteSTB(const Torque::Path& path, GBitmap* bitmap, U32 compressionLevel);
|
||||
|
||||
static struct _privateRegisterSTB
|
||||
{
|
||||
_privateRegisterSTB()
|
||||
{
|
||||
GBitmap::Registration reg;
|
||||
reg.priority = 100;
|
||||
|
||||
reg.extensions.push_back("png");
|
||||
reg.extensions.push_back("bmp");
|
||||
reg.extensions.push_back("jpg");
|
||||
reg.extensions.push_back("jpeg");
|
||||
reg.extensions.push_back("psd");
|
||||
reg.extensions.push_back("hdr");
|
||||
reg.extensions.push_back("tga");
|
||||
|
||||
reg.readFunc = sReadSTB;
|
||||
|
||||
reg.writeFunc = sWriteSTB;
|
||||
|
||||
// for png only.
|
||||
reg.defaultCompression = 6;
|
||||
|
||||
GBitmap::sRegisterFormat(reg);
|
||||
|
||||
}
|
||||
} sStaticRegisterSTB;
|
||||
|
||||
bool sReadSTB(const Torque::Path& path, GBitmap* bitmap)
|
||||
{
|
||||
PROFILE_SCOPE(sReadSTB);
|
||||
|
||||
S32 x, y, n, channels;
|
||||
String ext = path.getExtension();
|
||||
|
||||
U32 prevWaterMark = FrameAllocator::getWaterMark();
|
||||
|
||||
if (!stbi_info(path.getFullPath().c_str(), &x, &y, &channels))
|
||||
{
|
||||
FrameAllocator::setWaterMark(prevWaterMark);
|
||||
return false;
|
||||
}
|
||||
|
||||
// do this to map 2 channels to 4, 2 channel not supported by gbitmap yet..
|
||||
if (channels == 2)
|
||||
channels = 4;
|
||||
if (!ext.equal("png"))
|
||||
{
|
||||
if (stbi_is_16_bit(path.getFullPath().c_str()))
|
||||
{
|
||||
U16* data = stbi_load_16(path.getFullPath().c_str(), &x, &y, &n, channels);
|
||||
|
||||
// if succesful deal make the bitmap, else try other loaders.
|
||||
if (data)
|
||||
{
|
||||
GFXFormat format;
|
||||
if (n == 1)
|
||||
format = GFXFormatL16;
|
||||
else
|
||||
format = GFXFormatR16G16B16A16; // not sure if this is correct.
|
||||
|
||||
bitmap->deleteImage();
|
||||
|
||||
// actually allocate the bitmap space...
|
||||
bitmap->allocateBitmap(x, y,
|
||||
false, // don't extrude miplevels...
|
||||
format); // use determined format...
|
||||
|
||||
U16* pBase = (U16*)bitmap->getBits();
|
||||
|
||||
U32 rowBytes = bitmap->getByteSize();
|
||||
|
||||
dMemcpy(pBase, data, rowBytes);
|
||||
|
||||
stbi_image_free(data);
|
||||
|
||||
FrameAllocator::setWaterMark(prevWaterMark);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ext.equal("hdr"))
|
||||
{
|
||||
// force load to 4 channel.
|
||||
float* data = stbi_loadf(path.getFullPath().c_str(), &x, &y, &n, 4);
|
||||
|
||||
unsigned char* dataChar = stbi__hdr_to_ldr(data, x, y, 4);
|
||||
bitmap->deleteImage();
|
||||
// actually allocate the bitmap space...
|
||||
bitmap->allocateBitmap(x, y,
|
||||
false,
|
||||
GFXFormatR8G8B8A8);
|
||||
|
||||
U8* pBase = (U8*)bitmap->getBits();
|
||||
|
||||
U32 rowBytes = x * y * 4;
|
||||
|
||||
dMemcpy(pBase, dataChar, rowBytes);
|
||||
|
||||
stbi_image_free(dataChar);
|
||||
|
||||
FrameAllocator::setWaterMark(prevWaterMark);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
unsigned char* data = stbi_load(path.getFullPath().c_str(), &x, &y, &n, channels);
|
||||
|
||||
bitmap->deleteImage();
|
||||
|
||||
GFXFormat format;
|
||||
|
||||
switch (channels) {
|
||||
case 1:
|
||||
format = GFXFormatA8;
|
||||
break;
|
||||
case 2:
|
||||
format = GFXFormatA8L8;
|
||||
break;
|
||||
case 3:
|
||||
format = GFXFormatR8G8B8;
|
||||
break;
|
||||
case 4:
|
||||
format = GFXFormatR8G8B8A8;
|
||||
break;
|
||||
default:
|
||||
FrameAllocator::setWaterMark(prevWaterMark);
|
||||
return false;
|
||||
}
|
||||
|
||||
// actually allocate the bitmap space...
|
||||
bitmap->allocateBitmap(x, y,
|
||||
false, // don't extrude miplevels...
|
||||
format); // use determined format...
|
||||
|
||||
U8* pBase = (U8*)bitmap->getBits();
|
||||
|
||||
U32 rowBytes = bitmap->getByteSize();
|
||||
|
||||
dMemcpy(pBase, data, rowBytes);
|
||||
|
||||
stbi_image_free(data);
|
||||
// Check this bitmap for transparency
|
||||
if (channels == 4)
|
||||
bitmap->checkForTransparency();
|
||||
|
||||
FrameAllocator::setWaterMark(prevWaterMark);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Write bitmap to an image file.
|
||||
*
|
||||
* @param[in] path Destination image file path. File name extension determines image format.
|
||||
* ".bmp" for Microsoft Bitmap.
|
||||
* ".hdr" for High Dynamic Range (HDR).
|
||||
* ".jpg" or ".jpeg" for Joint Photographic Experts Group (JPEG).
|
||||
* ".png" for Portable Network Graphics (PNG).
|
||||
* ".tga" for Truevision TGA (TARGA).
|
||||
*
|
||||
*
|
||||
* @param[in] bitmap Source bitmap to encode image from.
|
||||
* @param compressionLevel Image format specific compression level.
|
||||
* For JPEG sets the quality level percentage, range 0 to 100.
|
||||
* Not used for other image formats.
|
||||
*/
|
||||
bool sWriteSTB(const Torque::Path& path, GBitmap* bitmap, U32 compressionLevel)
|
||||
{
|
||||
PROFILE_SCOPE(sWriteSTB);
|
||||
|
||||
// get our data to be saved.
|
||||
U32 width = bitmap->getWidth();
|
||||
U32 height = bitmap->getHeight();
|
||||
U32 bytes = bitmap->getBytesPerPixel();
|
||||
GFXFormat format = bitmap->getFormat();
|
||||
String ext = path.getExtension();
|
||||
|
||||
|
||||
|
||||
U32 stride = width * bytes;
|
||||
// we always have at least 1
|
||||
U32 comp = 1;
|
||||
|
||||
if (format == GFXFormatR8G8B8)
|
||||
{
|
||||
comp = 3;
|
||||
}
|
||||
else if (format == GFXFormatR8G8B8A8 || format == GFXFormatR8G8B8X8 || format == GFXFormatR8G8B8A8_LINEAR_FORCE)
|
||||
{
|
||||
comp = 4;
|
||||
}
|
||||
|
||||
if (ext.equal("png"))
|
||||
{
|
||||
stbi_write_png_compression_level = compressionLevel;
|
||||
if (stbi_write_png(path.getFullPath().c_str(), width, height, comp, bitmap->getWritableBits(), 0))
|
||||
return true;
|
||||
}
|
||||
|
||||
if (ext.equal("tga"))
|
||||
{
|
||||
if (stbi_write_tga(path.getFullPath().c_str(), width, height, comp, bitmap->getWritableBits()))
|
||||
return true;
|
||||
}
|
||||
|
||||
if (ext.equal("bmp"))
|
||||
{
|
||||
if (stbi_write_bmp(path.getFullPath().c_str(), width, height, comp, bitmap->getWritableBits()))
|
||||
return true;
|
||||
}
|
||||
|
||||
if (ext.equal("jpg") || ext.equal("jpeg"))
|
||||
{
|
||||
if (stbi_write_jpg(path.getFullPath().c_str(), width, height, comp, bitmap->getWritableBits(), compressionLevel))
|
||||
return true;
|
||||
}
|
||||
|
||||
if (ext.equal("hdr"))
|
||||
{
|
||||
if (stbi_write_hdr(path.getFullPath().c_str(), width, height, comp, (const F32*)bitmap->getWritableBits()))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
|
@ -1,491 +0,0 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "core/stream/stream.h"
|
||||
|
||||
#include "gfx/bitmap/gBitmap.h"
|
||||
|
||||
|
||||
static bool sReadTGA(Stream &stream, GBitmap *bitmap);
|
||||
static bool sWriteTGA(GBitmap *bitmap, Stream &stream, U32 compressionLevel);
|
||||
|
||||
static struct _privateRegisterTGA
|
||||
{
|
||||
_privateRegisterTGA()
|
||||
{
|
||||
GBitmap::Registration reg;
|
||||
|
||||
reg.extensions.push_back( "tga" );
|
||||
|
||||
reg.readFunc = sReadTGA;
|
||||
reg.writeFunc = sWriteTGA;
|
||||
|
||||
GBitmap::sRegisterFormat( reg );
|
||||
}
|
||||
} sStaticRegisterTGA;
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
//-------------------------------------- Supplementary I/O
|
||||
//
|
||||
|
||||
enum eImageType
|
||||
{
|
||||
TypeNoData = 0,
|
||||
TypeUncPaletted = 1,
|
||||
TypeUncTruecolor = 2,
|
||||
TypeUncGrayscale = 3,
|
||||
TypeRlePaletted = 9,
|
||||
TypeRleTruecolor = 10,
|
||||
TypeRleGrayscale = 11
|
||||
};
|
||||
|
||||
enum ePixelMap
|
||||
{
|
||||
MapLowerLeft = 0,
|
||||
MapLowerRight = 1,
|
||||
MapUpperLeft = 2,
|
||||
MapUpperRight = 3,
|
||||
};
|
||||
|
||||
static void tga_write_pixel_to_mem( U8 * dat, U8 img_spec, U32 number,
|
||||
U32 w, U32 h, U32 pixel, U32 bppOut )
|
||||
{
|
||||
// write the pixel to the data regarding how the
|
||||
// header says the data is ordered.
|
||||
|
||||
U32 x, y;
|
||||
|
||||
switch( (img_spec & 0x30) >> 4 )
|
||||
{
|
||||
case MapLowerRight:
|
||||
x = w - 1 - (number % w);
|
||||
y = h - 1 - (number / w);
|
||||
break;
|
||||
|
||||
case MapUpperLeft:
|
||||
x = number % w;
|
||||
y = number / w;
|
||||
break;
|
||||
|
||||
case MapUpperRight:
|
||||
x = w - 1 - (number % w);
|
||||
y = number / w;
|
||||
break;
|
||||
|
||||
case MapLowerLeft:
|
||||
default:
|
||||
x = number % w;
|
||||
y = h - 1 - (number / w);
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
U32 addy = (y * w + x) * bppOut;
|
||||
for ( U32 j = 0; j < bppOut; j++ )
|
||||
dat[addy + j] = (U8)((pixel >> (j * 8)) & 0xFF);
|
||||
}
|
||||
|
||||
static U32 tga_get_pixel( Stream& stream, U8 bppIn,
|
||||
U8 * colormap, U8 cmapBytesEntry )
|
||||
{
|
||||
/* get the image data value out */
|
||||
|
||||
U32 tmp_int32 = 0;
|
||||
|
||||
for ( U32 j = 0; j < bppIn; j++ )
|
||||
{
|
||||
U8 tmp_byte;
|
||||
if ( !stream.read( &tmp_byte ) )
|
||||
tmp_int32 = 0;
|
||||
else
|
||||
tmp_int32 += tmp_byte << (j * 8);
|
||||
}
|
||||
|
||||
/* byte-order correct the thing */
|
||||
switch( bppIn )
|
||||
{
|
||||
case 2:
|
||||
tmp_int32 = convertLEndianToHost( (U16)tmp_int32 );
|
||||
break;
|
||||
|
||||
case 3: /* intentional fall-thru */
|
||||
case 4:
|
||||
tmp_int32 = convertLEndianToHost( tmp_int32 );
|
||||
break;
|
||||
}
|
||||
|
||||
U32 tmp_col;
|
||||
|
||||
if ( colormap )
|
||||
{
|
||||
/* need to look up value to get real color */
|
||||
tmp_col = 0;
|
||||
for ( U32 j = 0; j < cmapBytesEntry; j++ )
|
||||
tmp_col += colormap[cmapBytesEntry * tmp_int32 + j] << (8 * j);
|
||||
}
|
||||
else
|
||||
{
|
||||
tmp_col = tmp_int32;
|
||||
}
|
||||
|
||||
return tmp_col;
|
||||
}
|
||||
|
||||
static U32 tga_convert_color( U32 pixel, U32 bppIn, U8 alphabits, U32 bppOut )
|
||||
{
|
||||
// this is not only responsible for converting from different depths
|
||||
// to other depths, it also switches BGR to RGB.
|
||||
|
||||
// this thing will also premultiply alpha, on a pixel by pixel basis.
|
||||
|
||||
U8 r, g, b, a;
|
||||
|
||||
switch( bppIn )
|
||||
{
|
||||
case 32:
|
||||
if ( alphabits == 0 )
|
||||
goto is_24_bit_in_disguise;
|
||||
// 32-bit to 32-bit -- nop.
|
||||
break;
|
||||
|
||||
case 24:
|
||||
is_24_bit_in_disguise:
|
||||
// 24-bit to 32-bit; (only force alpha to full)
|
||||
pixel |= 0xFF000000;
|
||||
break;
|
||||
|
||||
case 15:
|
||||
is_15_bit_in_disguise:
|
||||
r = (U8)(((F32)((pixel & 0x7C00) >> 10)) * 8.2258f);
|
||||
g = (U8)(((F32)((pixel & 0x03E0) >> 5 )) * 8.2258f);
|
||||
b = (U8)(((F32)(pixel & 0x001F)) * 8.2258f);
|
||||
// 15-bit to 32-bit; (force alpha to full)
|
||||
pixel = 0xFF000000 + (r << 16) + (g << 8) + b;
|
||||
break;
|
||||
|
||||
case 16:
|
||||
if ( alphabits == 1 )
|
||||
goto is_15_bit_in_disguise;
|
||||
|
||||
// 16-bit to 32-bit; (force alpha to full)
|
||||
r = (U8)(((F32)((pixel & 0xF800) >> 11)) * 8.2258f);
|
||||
g = (U8)(((F32)((pixel & 0x07E0) >> 5 )) * 4.0476f);
|
||||
b = (U8)(((F32)(pixel & 0x001F)) * 8.2258f);
|
||||
pixel = 0xFF000000 + (r << 16) + (g << 8) + b;
|
||||
break;
|
||||
}
|
||||
|
||||
// convert the 32-bit pixel from BGR to RGB.
|
||||
pixel = (pixel & 0xFF00FF00) + ((pixel & 0xFF) << 16) + ((pixel & 0xFF0000) >> 16);
|
||||
|
||||
r = pixel & 0x000000FF;
|
||||
g = (pixel & 0x0000FF00) >> 8;
|
||||
b = (pixel & 0x00FF0000) >> 16;
|
||||
a = (pixel & 0xFF000000) >> 24;
|
||||
|
||||
// not premultiplied alpha -- multiply.
|
||||
r = (U8)(((F32)r / 255.0f) * ((F32)a / 255.0f) * 255.0f);
|
||||
g = (U8)(((F32)g / 255.0f) * ((F32)a / 255.0f) * 255.0f);
|
||||
b = (U8)(((F32)b / 255.0f) * ((F32)a / 255.0f) * 255.0f);
|
||||
|
||||
pixel = r + (g << 8) + (b << 16) + (a << 24);
|
||||
|
||||
/* now convert from 32-bit to whatever they want. */
|
||||
switch( bppOut )
|
||||
{
|
||||
case 4:
|
||||
// 32 to 32 -- nop.
|
||||
break;
|
||||
|
||||
case 3:
|
||||
// 32 to 24 -- discard alpha.
|
||||
pixel &= 0x00FFFFFF;
|
||||
break;
|
||||
}
|
||||
|
||||
return pixel;
|
||||
}
|
||||
|
||||
static bool sReadTGA(Stream &stream, GBitmap *bitmap)
|
||||
{
|
||||
PROFILE_SCOPE(sReadTGA);
|
||||
struct Header
|
||||
{
|
||||
U8 idLength; // length of the image_id string below.
|
||||
U8 cmapType; // paletted image <=> cmapType
|
||||
U8 imageType; // can be any of the IMG_TYPE constants above.
|
||||
U16 cmapFirst; //
|
||||
U16 cmapLength; // how long the colormap is
|
||||
U8 cmapEntrySize; // how big a palette entry is.
|
||||
U16 xOrigin; // the x origin of the image in the image data.
|
||||
U16 yOrigin; // the y origin of the image in the image data.
|
||||
U16 width; // the width of the image.
|
||||
U16 height; // the height of the image.
|
||||
U8 pixelDepth; // the depth of a pixel in the image.
|
||||
U8 imageDesc; // the image descriptor.
|
||||
};
|
||||
|
||||
// Read header
|
||||
Header header;
|
||||
stream.read( &header.idLength );
|
||||
stream.read( &header.cmapType );
|
||||
stream.read( &header.imageType );
|
||||
stream.read( &header.cmapFirst );
|
||||
stream.read( &header.cmapLength );
|
||||
stream.read( &header.cmapEntrySize );
|
||||
stream.read( &header.xOrigin );
|
||||
stream.read( &header.yOrigin );
|
||||
stream.read( &header.width );
|
||||
stream.read( &header.height );
|
||||
stream.read( &header.pixelDepth );
|
||||
stream.read( &header.imageDesc );
|
||||
|
||||
U32 numPixels = header.width * header.height;
|
||||
if ( numPixels == 0 )
|
||||
{
|
||||
//Con::errorf( "Texture has width and/or height set to 0" );
|
||||
return false;
|
||||
}
|
||||
|
||||
U8 alphabits = header.imageDesc & 0x0F;
|
||||
|
||||
/* seek past the image id, if there is one */
|
||||
if ( header.idLength )
|
||||
{
|
||||
if ( !stream.setPosition( stream.getPosition() + header.idLength ) )
|
||||
{
|
||||
//Con::errorf( "Unexpected end of stream encountered" );
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/* if this is a 'nodata' image, just jump out. */
|
||||
if ( header.imageType == TypeNoData )
|
||||
{
|
||||
//Con::errorf( "Texture contains no data" );
|
||||
return false;
|
||||
}
|
||||
|
||||
/* deal with the colormap, if there is one. */
|
||||
U8* colormap = NULL;
|
||||
U32 cmapBytes = 0;
|
||||
U8 cmapBytesEntry = 0;
|
||||
|
||||
if ( header.cmapType )
|
||||
{
|
||||
switch( header.imageType )
|
||||
{
|
||||
case TypeUncPaletted:
|
||||
case TypeRlePaletted:
|
||||
break;
|
||||
|
||||
case TypeUncTruecolor:
|
||||
case TypeRleTruecolor:
|
||||
// this should really be an error, but some really old
|
||||
// crusty targas might actually be like this (created by TrueVision, no less!)
|
||||
// so, we'll hack our way through it.
|
||||
break;
|
||||
|
||||
case TypeUncGrayscale:
|
||||
case TypeRleGrayscale:
|
||||
//Con::errorf( "Found colormap for a grayscale image" );
|
||||
return false;
|
||||
}
|
||||
|
||||
/* ensure colormap entry size is something we support */
|
||||
if ( !(header.cmapEntrySize == 15 ||
|
||||
header.cmapEntrySize == 16 ||
|
||||
header.cmapEntrySize == 24 ||
|
||||
header.cmapEntrySize == 32) )
|
||||
{
|
||||
//Con::errorf( "Unsupported colormap entry size" );
|
||||
return false;
|
||||
}
|
||||
|
||||
/* allocate memory for a colormap */
|
||||
if ( header.cmapEntrySize & 0x07 )
|
||||
cmapBytesEntry = (((8 - (header.cmapEntrySize & 0x07)) + header.cmapEntrySize) >> 3);
|
||||
else
|
||||
cmapBytesEntry = (header.cmapEntrySize >> 3);
|
||||
|
||||
cmapBytes = cmapBytesEntry * header.cmapLength;
|
||||
colormap = new U8[ cmapBytes ];
|
||||
|
||||
for ( U32 i = 0; i < header.cmapLength; i++ )
|
||||
{
|
||||
/* seek ahead to first entry used */
|
||||
if ( header.cmapFirst != 0 )
|
||||
stream.setPosition( stream.getPosition() + header.cmapFirst * cmapBytesEntry );
|
||||
|
||||
U32 tmp_int32 = 0;
|
||||
for ( U32 j = 0; j < cmapBytesEntry; j++ )
|
||||
{
|
||||
U8 tmp_byte;
|
||||
if ( !stream.read( &tmp_byte ) )
|
||||
{
|
||||
delete [] colormap;
|
||||
//Con::errorf( "Bad colormap" );
|
||||
return false;
|
||||
}
|
||||
tmp_int32 += tmp_byte << (j * 8);
|
||||
}
|
||||
|
||||
// byte order correct.
|
||||
tmp_int32 = convertLEndianToHost( tmp_int32 );
|
||||
|
||||
for ( U32 j = 0; j < cmapBytesEntry; j++ )
|
||||
colormap[i * cmapBytesEntry + j] = (tmp_int32 >> (8 * j)) & 0xFF;
|
||||
}
|
||||
}
|
||||
|
||||
// compute number of bytes in an image data unit (either index or BGR triple)
|
||||
U8 inBytesPerPixel = 0;
|
||||
if ( header.pixelDepth & 0x07 )
|
||||
inBytesPerPixel = (((8 - (header.pixelDepth & 0x07)) + header.pixelDepth) >> 3);
|
||||
else
|
||||
inBytesPerPixel = (header.pixelDepth >> 3);
|
||||
|
||||
/* assume that there's one byte per pixel */
|
||||
if ( inBytesPerPixel == 0 )
|
||||
inBytesPerPixel = 1;
|
||||
|
||||
GFXFormat gfxFmt;
|
||||
U32 outBytesPerPixel;
|
||||
switch ( header.pixelDepth )
|
||||
{
|
||||
case 32:
|
||||
gfxFmt = GFXFormatR8G8B8A8;
|
||||
outBytesPerPixel = 4;
|
||||
break;
|
||||
|
||||
case 24:
|
||||
default:
|
||||
gfxFmt = GFXFormatR8G8B8;
|
||||
outBytesPerPixel = 3;
|
||||
break;
|
||||
}
|
||||
|
||||
bitmap->allocateBitmap( header.width, header.height, false, gfxFmt );
|
||||
|
||||
// compute the true number of bits per pixel
|
||||
U8 trueBitsPerPixel = header.cmapType ? header.cmapEntrySize : header.pixelDepth;
|
||||
|
||||
// Override the number of alpha bits if necessary
|
||||
// Some apps generate transparent TGAs with alphabits set to 0 in the image descriptor
|
||||
if ( ( trueBitsPerPixel == 32 ) && ( alphabits == 0 ) )
|
||||
alphabits = 8;
|
||||
|
||||
switch( header.imageType )
|
||||
{
|
||||
case TypeUncTruecolor:
|
||||
case TypeUncGrayscale:
|
||||
case TypeUncPaletted:
|
||||
|
||||
/* FIXME: support grayscale */
|
||||
|
||||
for ( U32 i = 0; i < numPixels; i++ )
|
||||
{
|
||||
// get the color value.
|
||||
U32 tmp_col = tga_get_pixel( stream, inBytesPerPixel, colormap, cmapBytesEntry );
|
||||
tmp_col = tga_convert_color( tmp_col, trueBitsPerPixel, alphabits, outBytesPerPixel );
|
||||
|
||||
// now write the data out.
|
||||
tga_write_pixel_to_mem( bitmap->getAddress( 0, 0 ), header.imageDesc,
|
||||
i, header.width, header.height, tmp_col, outBytesPerPixel );
|
||||
}
|
||||
break;
|
||||
|
||||
case TypeRleTruecolor:
|
||||
case TypeRleGrayscale:
|
||||
case TypeRlePaletted:
|
||||
|
||||
// FIXME: handle grayscale..
|
||||
|
||||
for ( U32 i = 0; i < numPixels; )
|
||||
{
|
||||
/* a bit of work to do to read the data.. */
|
||||
U8 packet_header;
|
||||
if ( !stream.read( 1, &packet_header ) )
|
||||
{
|
||||
// well, just let them fill the rest with null pixels then...
|
||||
packet_header = 1;
|
||||
}
|
||||
|
||||
if ( packet_header & 0x80 )
|
||||
{
|
||||
/* run length packet */
|
||||
U32 tmp_col = tga_get_pixel( stream, inBytesPerPixel, colormap, cmapBytesEntry );
|
||||
tmp_col = tga_convert_color( tmp_col, trueBitsPerPixel, alphabits, outBytesPerPixel );
|
||||
|
||||
U8 repcount = (packet_header & 0x7F) + 1;
|
||||
|
||||
/* write all the data out */
|
||||
for ( U32 j = 0; j < repcount; j++ )
|
||||
{
|
||||
tga_write_pixel_to_mem( bitmap->getAddress( 0, 0 ), header.imageDesc,
|
||||
i + j, header.width, header.height, tmp_col, outBytesPerPixel );
|
||||
}
|
||||
|
||||
i += repcount;
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
/* raw packet */
|
||||
/* get pixel from file */
|
||||
U8 repcount = (packet_header & 0x7F) + 1;
|
||||
|
||||
for ( U32 j = 0; j < repcount; j++ )
|
||||
{
|
||||
U32 tmp_col = tga_get_pixel( stream, inBytesPerPixel, colormap, cmapBytesEntry );
|
||||
tmp_col = tga_convert_color( tmp_col, trueBitsPerPixel, alphabits, outBytesPerPixel );
|
||||
|
||||
tga_write_pixel_to_mem( bitmap->getAddress( 0, 0 ), header.imageDesc,
|
||||
i + j, header.width, header.height, tmp_col, outBytesPerPixel );
|
||||
}
|
||||
|
||||
i += repcount;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
//Con::errorf( "Unknown image type" );
|
||||
delete[] colormap;
|
||||
return false;
|
||||
}
|
||||
|
||||
delete [] colormap;
|
||||
|
||||
// 32-bit tgas have an alpha channel
|
||||
bitmap->setHasTransparency( header.pixelDepth == 32 );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool sWriteTGA(GBitmap *bitmap, Stream &stream, U32 compressionLevel)
|
||||
{
|
||||
AssertISV(false, "GBitmap::writeTGA - doesn't support writing tga files!");
|
||||
|
||||
return false;
|
||||
}
|
||||
7987
Engine/source/gfx/bitmap/loaders/stb/stb_image.h
Normal file
7987
Engine/source/gfx/bitmap/loaders/stb/stb_image.h
Normal file
File diff suppressed because it is too large
Load diff
10303
Engine/source/gfx/bitmap/loaders/stb/stb_image_resize2.h
Normal file
10303
Engine/source/gfx/bitmap/loaders/stb/stb_image_resize2.h
Normal file
File diff suppressed because it is too large
Load diff
1724
Engine/source/gfx/bitmap/loaders/stb/stb_image_write.h
Normal file
1724
Engine/source/gfx/bitmap/loaders/stb/stb_image_write.h
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -699,7 +699,8 @@ bool GFont::read(Stream& io_rStream)
|
|||
for(i = 0; i < numSheets; i++)
|
||||
{
|
||||
GBitmap *bmp = new GBitmap;
|
||||
if(!bmp->readBitmap("png", io_rStream))
|
||||
String path = String::ToString("%s/%s %d %d (%s).png", Con::getVariable("$GUI::fontCacheDirectory"), mFaceName.c_str(), mSize, i, getCharSetName(mCharSet));
|
||||
if(!bmp->readBitmap("png", path))
|
||||
{
|
||||
delete bmp;
|
||||
return false;
|
||||
|
|
@ -775,8 +776,11 @@ bool GFont::write(Stream& stream)
|
|||
}
|
||||
|
||||
stream.write(mTextureSheets.size());
|
||||
for(i = 0; i < mTextureSheets.size(); i++)
|
||||
mTextureSheets[i].getBitmap()->writeBitmap("png", stream);
|
||||
for (i = 0; i < mTextureSheets.size(); i++)
|
||||
{
|
||||
String path = String::ToString("%s/%s %d %d (%s).png", Con::getVariable("$GUI::fontCacheDirectory"), mFaceName.c_str(), mSize, i, getCharSetName(mCharSet));
|
||||
mTextureSheets[i].getBitmap()->writeBitmap("png", path);
|
||||
}
|
||||
|
||||
stream.write(mCurX);
|
||||
stream.write(mCurY);
|
||||
|
|
@ -865,20 +869,9 @@ void GFont::exportStrip(const char *fileName, U32 padding, U32 kerning)
|
|||
// Advance.
|
||||
curWidth += mCharInfoList[i].width + kerning + 2*padding;
|
||||
}
|
||||
|
||||
// Write the image!
|
||||
FileStream fs;
|
||||
|
||||
fs.open( fileName, Torque::FS::File::Write );
|
||||
|
||||
if(fs.getStatus() != Stream::Ok)
|
||||
{
|
||||
Con::errorf("GFont::exportStrip - failed to open '%s' for writing.", fileName);
|
||||
return;
|
||||
}
|
||||
|
||||
// Done!
|
||||
gb.writeBitmap("png", fs);
|
||||
gb.writeBitmap("png", fileName);
|
||||
}
|
||||
|
||||
void GFont::setPlatformFont(PlatformFont *inPlatformFont)
|
||||
|
|
|
|||
|
|
@ -246,14 +246,10 @@ U32 GFXTextureObject::getEstimatedSizeInBytes() const
|
|||
|
||||
bool GFXTextureObject::dumpToDisk( const String &bmType, const String &path )
|
||||
{
|
||||
FileStream stream;
|
||||
if ( !stream.open( path, Torque::FS::File::Write ) )
|
||||
return false;
|
||||
|
||||
if ( mBitmap )
|
||||
return mBitmap->writeBitmap( bmType, stream );
|
||||
return mBitmap->writeBitmap( bmType, path);
|
||||
|
||||
GBitmap bitmap( getWidth(), getHeight(), false, getFormat() );
|
||||
copyToBmp( &bitmap );
|
||||
return bitmap.writeBitmap( bmType, stream );
|
||||
return bitmap.writeBitmap( bmType, path);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -126,89 +126,75 @@ void ScreenShot::capture( GuiCanvas *canvas )
|
|||
|
||||
// Open up the file on disk.
|
||||
dSprintf( filename, 256, "%s.%s", mFilename, "png" );
|
||||
FileStream fs;
|
||||
if ( !fs.open( filename, Torque::FS::File::Write ) )
|
||||
Con::errorf( "ScreenShot::capture() - Failed to open output file '%s'!", filename );
|
||||
|
||||
// Open a PNG stream for the final image
|
||||
DeferredPNGWriter pngWriter;
|
||||
pngWriter.begin(outBuffer->getFormat(), outBuffer->getWidth(), canvasSize.y * mTiles - overlapPixels.y * mTiles * 2, fs, 0);
|
||||
|
||||
// Render each tile to generate a huge screenshot.
|
||||
for( U32 ty=0; ty < mTiles; ty++ )
|
||||
//// Render each tile to generate a huge screenshot.
|
||||
for (U32 ty = 0; ty < mTiles; ty++)
|
||||
{
|
||||
for( S32 tx=0; tx < mTiles; tx++ )
|
||||
for (S32 tx = 0; tx < mTiles; tx++)
|
||||
{
|
||||
// Set the current tile offset for tileFrustum().
|
||||
mCurrTile.set( tx, mTiles - ty - 1 );
|
||||
mCurrTile.set(tx, mTiles - ty - 1);
|
||||
|
||||
// Let the canvas render the scene.
|
||||
canvas->renderFrame( false );
|
||||
|
||||
canvas->renderFrame(false);
|
||||
// Now grab the current back buffer.
|
||||
GBitmap *gb = _captureBackBuffer();
|
||||
GBitmap* gb = _captureBackBuffer();
|
||||
|
||||
// The current GFX device couldn't capture the backbuffer or it's unable of doing so.
|
||||
if (gb == NULL)
|
||||
return;
|
||||
// The current GFX device couldn't capture the backbuffer or it's unable of doing so.
|
||||
if (gb == NULL)
|
||||
return;
|
||||
|
||||
|
||||
// Copy the captured bitmap into its tile
|
||||
// within the output bitmap.
|
||||
const U32 inStride = gb->getWidth() * gb->getBytesPerPixel();
|
||||
const U8 *inColor = gb->getBits() + inStride * overlapPixels.y;
|
||||
const U32 inStride = gb->getWidth() * gb->getBytesPerPixel();
|
||||
const U8* inColor = gb->getBits() + inStride * overlapPixels.y;
|
||||
const U32 outStride = outBuffer->getWidth() * outBuffer->getBytesPerPixel();
|
||||
const U32 inOverlapOffset = overlapPixels.x * gb->getBytesPerPixel();
|
||||
const U32 inOverlapStride = overlapPixels.x * gb->getBytesPerPixel()*2;
|
||||
const U32 outOffset = (tx * (gb->getWidth() - overlapPixels.x*2 )) * gb->getBytesPerPixel();
|
||||
U8 *outColor = outBuffer->getWritableBits() + outOffset;
|
||||
for( U32 row=0; row < gb->getHeight() - overlapPixels.y; row++ )
|
||||
const U32 inOverlapOffset = overlapPixels.x * gb->getBytesPerPixel();
|
||||
const U32 inOverlapStride = overlapPixels.x * gb->getBytesPerPixel() * 2;
|
||||
const U32 outOffset = (tx * (gb->getWidth() - overlapPixels.x * 2)) * gb->getBytesPerPixel();
|
||||
U8 * outColor = outBuffer->getWritableBits() + outOffset;
|
||||
for (U32 row = 0; row < gb->getHeight() - overlapPixels.y; row++)
|
||||
{
|
||||
dMemcpy( outColor, inColor + inOverlapOffset, inStride - inOverlapStride );
|
||||
|
||||
//Grandient blend the left overlap area of this tile over the previous tile left border
|
||||
dMemcpy(outColor, inColor + inOverlapOffset, inStride - inOverlapStride);
|
||||
|
||||
//Grandient blend the left overlap area of this tile over the previous tile left borde
|
||||
if (tx && !(ty && row < overlapPixels.y))
|
||||
{
|
||||
U8 *blendOverlapSrc = (U8*)inColor;
|
||||
U8 *blendOverlapDst = outColor - inOverlapOffset;
|
||||
for ( U32 px=0; px < overlapPixels.x; px++)
|
||||
U8* blendOverlapSrc = (U8*)inColor;
|
||||
U8* blendOverlapDst = outColor - inOverlapOffset;
|
||||
for (U32 px = 0; px < overlapPixels.x; px++)
|
||||
{
|
||||
F32 blendFactor = (F32)px / (F32)overlapPixels.x;
|
||||
sBlendPixelRGB888(blendOverlapSrc, blendOverlapDst, blendFactor);
|
||||
sBlendPixelRGB888(blendOverlapSrc, blendOverlapDst, blendFactor);
|
||||
|
||||
blendOverlapSrc += gb->getBytesPerPixel();
|
||||
blendOverlapDst += outBuffer->getBytesPerPixel();
|
||||
}
|
||||
blendOverlapDst += outBuffer->getBytesPerPixel();
|
||||
}
|
||||
}
|
||||
|
||||
//Gradient blend against the rows the excess overlap rows already in the buffer
|
||||
//Gradient blend against the rows the excess overlap rows already in the buffer
|
||||
if (ty && row < overlapPixels.y)
|
||||
{
|
||||
F32 rowBlendFactor = (F32)row / (F32)overlapPixels.y;
|
||||
U8 *blendSrc = outColor + outStride * (outBuffer->getHeight() - overlapPixels.y);
|
||||
U8 *blendDst = outColor;
|
||||
for ( U32 px=0; px < gb->getWidth() - overlapPixels.x*2; px++)
|
||||
{
|
||||
sBlendPixelRGB888(blendSrc, blendDst, 1.0-rowBlendFactor);
|
||||
U8* blendSrc = outColor + outStride * (outBuffer->getHeight() - overlapPixels.y);
|
||||
U8* blendDst = outColor;
|
||||
for (U32 px = 0; px < gb->getWidth() - overlapPixels.x * 2; px++)
|
||||
{
|
||||
sBlendPixelRGB888(blendSrc, blendDst, 1.0 - rowBlendFactor);
|
||||
blendSrc += gb->getBytesPerPixel();
|
||||
blendDst += outBuffer->getBytesPerPixel();
|
||||
}
|
||||
blendDst += outBuffer->getBytesPerPixel();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
inColor += inStride;
|
||||
outColor += outStride;
|
||||
}
|
||||
|
||||
delete gb;
|
||||
}
|
||||
|
||||
// Write the captured tile row into the PNG stream
|
||||
pngWriter.append(outBuffer, outBuffer->getHeight()-overlapPixels.y);
|
||||
}
|
||||
|
||||
//Close the PNG stream
|
||||
pngWriter.end();
|
||||
outBuffer->writeBitmap("png", filename);
|
||||
|
||||
// We captured... clear the flag.
|
||||
mPending = false;
|
||||
|
|
@ -234,20 +220,11 @@ void ScreenShot::_singleCapture( GuiCanvas *canvas )
|
|||
char filename[256];
|
||||
dSprintf( filename, 256, "%s.%s", mFilename, mWriteJPG ? "jpg" : "png" );
|
||||
|
||||
// Open up the file on disk.
|
||||
FileStream fs;
|
||||
if ( !fs.open( filename, Torque::FS::File::Write ) )
|
||||
Con::errorf( "ScreenShot::_singleCapture() - Failed to open output file '%s'!", filename );
|
||||
// Write it and close.
|
||||
if ( mWriteJPG )
|
||||
bitmap->writeBitmap( "jpg", filename);
|
||||
else
|
||||
{
|
||||
// Write it and close.
|
||||
if ( mWriteJPG )
|
||||
bitmap->writeBitmap( "jpg", fs );
|
||||
else
|
||||
bitmap->writeBitmap( "png", fs );
|
||||
|
||||
fs.close();
|
||||
}
|
||||
bitmap->writeBitmap( "png", filename);
|
||||
|
||||
// Cleanup.
|
||||
delete bitmap;
|
||||
|
|
|
|||
|
|
@ -44,18 +44,12 @@ public:
|
|||
/// Pushes a new frame into the video stream
|
||||
bool pushFrame( GBitmap * bitmap )
|
||||
{
|
||||
FileStream fs;
|
||||
String framePath = mPath + String::ToString("%.6u.png", mCurrentFrame);
|
||||
if ( !fs.open( framePath, Torque::FS::File::Write ) )
|
||||
{
|
||||
Con::errorf( "VideoEncoderPNG::pushFrame() - Failed to open output file '%s'!", framePath.c_str() );
|
||||
return false;
|
||||
}
|
||||
|
||||
//Increment
|
||||
mCurrentFrame++;
|
||||
|
||||
bool result = bitmap->writeBitmap("png", fs, 0);
|
||||
bool result = bitmap->writeBitmap("png", framePath, 0);
|
||||
pushProcessedBitmap(bitmap);
|
||||
|
||||
return result;
|
||||
|
|
@ -73,4 +67,4 @@ public:
|
|||
}
|
||||
};
|
||||
|
||||
REGISTER_VIDEO_ENCODER(VideoEncoderPNG, PNG)
|
||||
REGISTER_VIDEO_ENCODER(VideoEncoderPNG, PNG)
|
||||
|
|
|
|||
|
|
@ -79,7 +79,7 @@ GuiWindowCtrl::GuiWindowCtrl()
|
|||
mResizeEdge(edgeNone),
|
||||
mResizeHeight(true),
|
||||
mCanMove(true),
|
||||
mResizeMargin(2.f),
|
||||
mResizeMargin(5.f),
|
||||
mCanClose(true),
|
||||
mCanMinimize(true),
|
||||
mCanMaximize(true),
|
||||
|
|
@ -143,6 +143,8 @@ void GuiWindowCtrl::initPersistFields()
|
|||
"Whether the window can be resized horizontally." );
|
||||
addField( "resizeHeight", TypeBool, Offset( mResizeHeight, GuiWindowCtrl ),
|
||||
"Whether the window can be resized vertically." );
|
||||
addField("resizeMargin", TypeF32, Offset(mResizeMargin, GuiWindowCtrl),
|
||||
"Margin along the window edge to allow grabbing.");
|
||||
addField( "canMove", TypeBool, Offset( mCanMove, GuiWindowCtrl ),
|
||||
"Whether the window can be moved by dragging its titlebar." );
|
||||
addField( "canClose", TypeBool, Offset( mCanClose, GuiWindowCtrl ),
|
||||
|
|
@ -1490,7 +1492,7 @@ const RectI GuiWindowCtrl::getClientRect()
|
|||
// Finally, inset it by padding
|
||||
// Inset by padding. margin is specified for all t/b/l/r but
|
||||
// uses only pointx pointy uniformly on both ends. This should be fixed. - JDD
|
||||
// winRect.inset( mSizingOptions.mPadding.point.x, mSizingOptions.mPadding.point.y );
|
||||
winRect.inset(mResizeMargin, mResizeMargin);
|
||||
|
||||
return winRect;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -71,7 +71,7 @@ bool blTerrainChunk::read(Stream & stream)
|
|||
return(false);
|
||||
|
||||
mLightmap = new GBitmap();
|
||||
return mLightmap->readBitmap("png",stream);
|
||||
return false;//mLightmap->readBitmap("png",stream);
|
||||
}
|
||||
|
||||
bool blTerrainChunk::write(Stream & stream)
|
||||
|
|
@ -82,8 +82,8 @@ bool blTerrainChunk::write(Stream & stream)
|
|||
if(!mLightmap)
|
||||
return(false);
|
||||
|
||||
if(!mLightmap->writeBitmap("png",stream))
|
||||
return(false);
|
||||
//if(!mLightmap->writeBitmap("png",stream))
|
||||
//return(false);
|
||||
|
||||
return(true);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -39,9 +39,10 @@ SFXMemoryStream::SFXMemoryStream( const SFXFormat& format,
|
|||
|
||||
void SFXMemoryStream::reset()
|
||||
{
|
||||
if( dynamic_cast< IResettable* >( getSourceStream() ) )
|
||||
IResettable* rStream = dynamic_cast<IResettable*>(getSourceStream());
|
||||
if(rStream )
|
||||
{
|
||||
reinterpret_cast< IResettable* >( getSourceStream() )->reset();
|
||||
rStream->reset();
|
||||
|
||||
if( mCurrentPacket )
|
||||
destructSingle( mCurrentPacket );
|
||||
|
|
|
|||
|
|
@ -67,14 +67,7 @@ bool TerrainBlock::exportHeightMap( const UTF8 *filePath, const String &format )
|
|||
}
|
||||
}
|
||||
|
||||
FileStream stream;
|
||||
if ( !stream.open( filePath, Torque::FS::File::Write ) )
|
||||
{
|
||||
Con::errorf( "TerrainBlock::exportHeightMap() - Error opening file for writing: %s !", filePath );
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( !output.writeBitmap( format, stream ) )
|
||||
if ( !output.writeBitmap( format, filePath) )
|
||||
{
|
||||
Con::errorf( "TerrainBlock::exportHeightMap() - Error writing %s: %s !", format.c_str(), filePath );
|
||||
return false;
|
||||
|
|
@ -120,14 +113,7 @@ bool TerrainBlock::exportLayerMaps( const UTF8 *filePrefix, const String &format
|
|||
UTF8 filePath[1024];
|
||||
dSprintf( filePath, 1024, "%s_%d_%s.%s", filePrefix, i, mFile->mMaterials[i]->getInternalName(), format.c_str() );
|
||||
|
||||
FileStream stream;
|
||||
if ( !stream.open( filePath, Torque::FS::File::Write ) )
|
||||
{
|
||||
Con::errorf( "TerrainBlock::exportLayerMaps() - Error opening file for writing: %s !", filePath );
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( !output.writeBitmap( format, stream ) )
|
||||
if ( !output.writeBitmap( format, filePath) )
|
||||
{
|
||||
Con::errorf( "TerrainBlock::exportLayerMaps() - Error writing %s: %s !", format.c_str(), filePath );
|
||||
return false;
|
||||
|
|
|
|||
|
|
@ -477,16 +477,9 @@ void TerrainBlock::_updateBaseTexture(bool writeToCache)
|
|||
}
|
||||
else
|
||||
{
|
||||
FileStream stream;
|
||||
if (!stream.open(_getBaseTexCacheFileName(), Torque::FS::File::Write))
|
||||
{
|
||||
mBaseTex = blendTex;
|
||||
return;
|
||||
}
|
||||
|
||||
GBitmap bitmap(blendTex->getWidth(), blendTex->getHeight(), false, GFXFormatR8G8B8A8);
|
||||
blendTex->copyToBmp(&bitmap);
|
||||
bitmap.writeBitmap(formatToExtension(mBaseTexFormat), stream);
|
||||
bitmap.writeBitmap(formatToExtension(mBaseTexFormat), _getBaseTexCacheFileName());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -504,14 +504,10 @@ void TSLastDetail::_update()
|
|||
String imposterPath = _getDiffuseMapPath();
|
||||
String normalsPath = _getNormalMapPath();
|
||||
|
||||
FileStream stream;
|
||||
if ( stream.open( imposterPath, Torque::FS::File::Write ) )
|
||||
destBmp.writeBitmap( "png", stream );
|
||||
stream.close();
|
||||
|
||||
if ( stream.open( normalsPath, Torque::FS::File::Write ) )
|
||||
destNormal.writeBitmap( "png", stream );
|
||||
stream.close();
|
||||
if (!destBmp.writeBitmap("png", imposterPath))
|
||||
Con::errorf("TSLastDetail::_update() - failed to write imposter %s", imposterPath.c_str());
|
||||
if (!destNormal.writeBitmap("png", normalsPath))
|
||||
Con::errorf("TSLastDetail::_update() - failed to write normal %s", normalsPath.c_str());
|
||||
}
|
||||
|
||||
// DEBUG: Some code to force usage of a test image.
|
||||
|
|
|
|||
|
|
@ -316,16 +316,9 @@ void ImposterCapture::_separateAlpha( GBitmap *imposterOut )
|
|||
|
||||
if ( 0 )
|
||||
{
|
||||
FileStream fs;
|
||||
if ( fs.open( "./imposterout.png", Torque::FS::File::Write ) )
|
||||
imposterOut->writeBitmap( "png", fs );
|
||||
imposterOut->writeBitmap("png", "./imposterout.png");
|
||||
|
||||
fs.close();
|
||||
|
||||
if ( fs.open( "./temp.png", Torque::FS::File::Write ) )
|
||||
bmp->writeBitmap( "png", fs );
|
||||
|
||||
fs.close();
|
||||
bmp->writeBitmap("png", "./temp.png");
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -482,26 +475,13 @@ void ImposterCapture::capture( const MatrixF &rotMatrix,
|
|||
if ( 0 )
|
||||
{
|
||||
// Render out the bitmaps for debug purposes.
|
||||
FileStream fs;
|
||||
if ( fs.open( "./blackbmp.png", Torque::FS::File::Write ) )
|
||||
mBlackBmp->writeBitmap( "png", fs );
|
||||
mBlackBmp->writeBitmap( "png", "./blackbmp.png" );
|
||||
|
||||
fs.close();
|
||||
mWhiteBmp->writeBitmap( "png", "./whitebmp.png" );
|
||||
|
||||
if ( fs.open( "./whitebmp.png", Torque::FS::File::Write ) )
|
||||
mWhiteBmp->writeBitmap( "png", fs );
|
||||
(*normalMapOut)->writeBitmap( "png", "./normalbmp.png" );
|
||||
|
||||
fs.close();
|
||||
|
||||
if ( fs.open( "./normalbmp.png", Torque::FS::File::Write ) )
|
||||
(*normalMapOut)->writeBitmap( "png", fs );
|
||||
|
||||
fs.close();
|
||||
|
||||
if ( fs.open( "./finalimposter.png", Torque::FS::File::Write ) )
|
||||
(*imposterOut)->writeBitmap( "png", fs );
|
||||
|
||||
fs.close();
|
||||
(*imposterOut)->writeBitmap( "png", "./finalimposter.png" );
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -37,6 +37,9 @@ function Prototyping::onDestroyGameServer(%this)
|
|||
//This is called when the client is initially set up by the game application
|
||||
function Prototyping::initClient(%this)
|
||||
{
|
||||
//class method prototyping
|
||||
%this.queueExec("./UI/classPrototyping");
|
||||
%this.queueExec("./UI/classPrototyping.gui");
|
||||
}
|
||||
|
||||
//This is called when a client connects to a server
|
||||
|
|
|
|||
|
|
@ -0,0 +1,5 @@
|
|||
<GUIAsset
|
||||
AssetName="classPrototyping"
|
||||
ScriptFile="@assetFile=classPrototyping.tscript"
|
||||
GUIFile="@assetFile=classPrototyping.gui"
|
||||
VersionId="1"/>
|
||||
115
Templates/BaseGame/game/data/Prototyping/UI/classPrototyping.gui
Normal file
115
Templates/BaseGame/game/data/Prototyping/UI/classPrototyping.gui
Normal file
|
|
@ -0,0 +1,115 @@
|
|||
//--- OBJECT WRITE BEGIN ---
|
||||
$guiContent = new GuiControl(classPrototyping) {
|
||||
extent = "1024 768";
|
||||
profile = "GuiDefaultProfile";
|
||||
tooltipProfile = "GuiToolTipProfile";
|
||||
isContainer = "1";
|
||||
canSaveDynamicFields = "1";
|
||||
originalAssetName = "classPrototyping";
|
||||
|
||||
new GuiWindowCtrl() {
|
||||
text = "Class Prototyping";
|
||||
position = "216 124";
|
||||
extent = "592 519";
|
||||
horizSizing = "center";
|
||||
vertSizing = "center";
|
||||
profile = "ToolsGuiWindowProfile";
|
||||
tooltipProfile = "GuiToolTipProfile";
|
||||
closeCommand = "Canvas.popDialog(classPrototyping);";
|
||||
new GuiScrollCtrl() {
|
||||
lockVertScroll = "1";
|
||||
position = "14 30";
|
||||
extent = "564 33";
|
||||
profile = "ToolsGuiScrollProfile";
|
||||
tooltipProfile = "GuiToolTipProfile";
|
||||
|
||||
new GuiDynamicCtrlArrayControl(ClassInheritanceListCtrl) {
|
||||
colCount = "1";
|
||||
colSize = "80";
|
||||
rowCount = "1";
|
||||
rowSize = "18";
|
||||
autoCellSize = "1";
|
||||
fillRowFirst = "0";
|
||||
dynamicSize = "1";
|
||||
position = "1 1";
|
||||
extent = "400 18";
|
||||
profile = "GuiDefaultProfile";
|
||||
tooltipProfile = "GuiToolTipProfile";
|
||||
};
|
||||
};
|
||||
new GuiTextCtrl() {
|
||||
text = "Callbacks";
|
||||
position = "24 66";
|
||||
extent = "54 14";
|
||||
profile = "ToolsGuiTextProfile";
|
||||
tooltipProfile = "GuiToolTipProfile";
|
||||
};
|
||||
new GuiScrollCtrl() {
|
||||
position = "19 80";
|
||||
extent = "552 326";
|
||||
profile = "ToolsGuiScrollProfile";
|
||||
tooltipProfile = "GuiToolTipProfile";
|
||||
|
||||
new GuiDynamicCtrlArrayControl(ClassMethodListCtrl) {
|
||||
colCount = "1";
|
||||
colSize = "8000";
|
||||
rowCount = "1";
|
||||
rowSize = "18";
|
||||
dynamicSize = "1";
|
||||
extent = "552 326";
|
||||
profile = "GuiDefaultProfile";
|
||||
tooltipProfile = "GuiToolTipProfile";
|
||||
};
|
||||
};
|
||||
new GuiBitmapButtonCtrl() {
|
||||
BitmapAsset = "ToolsModule:iconOpen_image";
|
||||
bitmapMode = "Centered";
|
||||
position = "348 467";
|
||||
extent = "22 22";
|
||||
horizSizing = "left";
|
||||
profile = "ToolsGuiButtonProfile";
|
||||
command = "SelectAssetPath.showDialog(AssetBrowser.dirHandler.currentAddress, \"setProtoTypeFilePath\");\nSelectAssetPathWindow.selectWindow();";
|
||||
tooltipProfile = "GuiToolTipProfile";
|
||||
tooltip = "New Module";
|
||||
};
|
||||
new GuiTextEditCtrl() {
|
||||
text = "data/ExampleModule";
|
||||
position = "143 470";
|
||||
extent = "201 20";
|
||||
horizSizing = "width";
|
||||
profile = "ToolsGuiTextEditProfile";
|
||||
active = "0";
|
||||
tooltipProfile = "GuiToolTipProfile";
|
||||
internalName = "targetPath";
|
||||
};
|
||||
new GuiTextCtrl() {
|
||||
text = "Target Path:";
|
||||
position = "20 470";
|
||||
extent = "116 17";
|
||||
profile = "ToolsGuiTextProfile";
|
||||
tooltipProfile = "GuiToolTipProfile";
|
||||
};
|
||||
new GuiButtonCtrl() {
|
||||
text = "Save";
|
||||
position = "431 465";
|
||||
profile = "ToolsGuiButtonProfile";
|
||||
command = "classPrototyping.writeResults();";
|
||||
tooltipProfile = "GuiToolTipProfile";
|
||||
};
|
||||
new GuiCheckBoxCtrl(ReportCommands) {
|
||||
text = "Report Commands";
|
||||
position = "16 420";
|
||||
extent = "125 30";
|
||||
profile = "ToolsGuiCheckBoxProfile";
|
||||
tooltipProfile = "GuiToolTipProfile";
|
||||
};
|
||||
new GuiCheckBoxCtrl(ReportVariables) {
|
||||
text = "Report Stock Variables";
|
||||
position = "152 420";
|
||||
extent = "125 30";
|
||||
profile = "ToolsGuiCheckBoxProfile";
|
||||
tooltipProfile = "GuiToolTipProfile";
|
||||
};
|
||||
};
|
||||
};
|
||||
//--- OBJECT WRITE END ---
|
||||
|
|
@ -0,0 +1,204 @@
|
|||
function classPrototyping::onWake(%this)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
function classPrototyping::onSleep(%this)
|
||||
{
|
||||
|
||||
}
|
||||
//PrototypeClass(MainMenuGui)
|
||||
//PrototypeClass(GuiChunkedBitmapCtrl)
|
||||
function PrototypeClass(%classInstance)
|
||||
{
|
||||
if (!isObject(%classInstance)) return;
|
||||
Canvas.pushDialog(classPrototyping);
|
||||
classPrototyping.fillClasslist(%classInstance);
|
||||
classPrototyping.SetNamespaceUsed(%classInstance);
|
||||
}
|
||||
|
||||
function classPrototyping::fillClasslist(%this, %classInstance)
|
||||
{
|
||||
ClassInheritanceListCtrl.deleteAllObjects();
|
||||
%this.instanceName = %classInstance.getName();
|
||||
|
||||
//get potentially scripted namespaces
|
||||
%class = %classInstance.getClassName();
|
||||
%prepend = "";
|
||||
if (%classInstance.getName() !$= "")
|
||||
%prepend = %classInstance.getName();
|
||||
if (%classInstance.class !$= "")
|
||||
%prepend = %prepend SPC %classInstance.class;
|
||||
if (%classInstance.superclass !$= "")
|
||||
%prepend = %prepend SPC %classInstance.superclass;
|
||||
|
||||
//append to hardcoded potential namespaces
|
||||
%this.classlist = %prepend SPC getClassHierarchy(%class);
|
||||
%this.classCount = getWordCount(%this.classlist);
|
||||
for (%i=0; %i<%this.classCount; %i++)
|
||||
{
|
||||
%inheritanceOrder = %this.classCount-(%i+1);
|
||||
%className = getWord(%this.classlist,%inheritanceOrder);
|
||||
if (%i<%this.classCount-1)
|
||||
%className = %className @"->";
|
||||
%elemClass = new GuiRadioCtrl("ProtoClassSelect"@ %i) {
|
||||
text = %className;
|
||||
entry = strreplace(%className,"->","");
|
||||
groupNum = "1";
|
||||
extent = "80 18";
|
||||
profile = "ToolsGuiRadioProfile";
|
||||
tooltipProfile = "GuiToolTipProfile";
|
||||
};
|
||||
eval("function ProtoClassSelect"@ %i @"::onClick(%this){classPrototyping.SetNamespaceUsed(%this.entry);}");
|
||||
ClassInheritanceListCtrl.addGuiControl(%elemClass);
|
||||
}
|
||||
%lastElem = "ProtoClassSelect"@ %this.classCount-1;
|
||||
%lastElem.setStateOn(true);
|
||||
}
|
||||
|
||||
function classPrototyping::SetNamespaceUsed(%this, %nameSpaceUsed)
|
||||
{
|
||||
ClassMethodListCtrl.deleteAllObjects();
|
||||
%this.fillMethodlist(%nameSpaceUsed);
|
||||
}
|
||||
|
||||
function classPrototyping::fillMethodlist(%this, %nameSpaceUsed)
|
||||
{
|
||||
ClassMethodListCtrl.deleteAllObjects();
|
||||
%this.nameSpaceUsed = %nameSpaceUsed;
|
||||
%this.methodArray = getMethodSigsNS(%nameSpaceUsed);
|
||||
%this.methodCount = %this.methodArray.count();
|
||||
|
||||
for (%i=0; %i<%this.methodCount; %i++)
|
||||
{
|
||||
%methodDef = getRecord(%this.methodArray.getValue(%i),0);
|
||||
%methodName = strreplace(%methodDef,"::"," ");
|
||||
%methodName = getWord(strreplace(%methodName,"("," "),2);
|
||||
|
||||
%elemMethod = new GuiCheckBoxCtrl("ProtoMethodSelect"@ %i) {
|
||||
text = %methodName;
|
||||
position = "1 1";
|
||||
profile = "ToolsGuiCheckBoxProfile";
|
||||
tooltipProfile = "GuiToolTipProfile";
|
||||
};
|
||||
ClassMethodListCtrl.addGuiControl(%elemMethod);
|
||||
}
|
||||
}
|
||||
|
||||
function setProtoTypeFilePath(%targetPath)
|
||||
{
|
||||
classPrototyping-->targetPath.text = %targetPath;
|
||||
}
|
||||
|
||||
function classPrototyping::readExistingLayout(%this)
|
||||
{
|
||||
for (%i=0; %i<%this.classCount; %i++)
|
||||
{
|
||||
%inheritanceOrder = %this.classCount-(%i+1);
|
||||
%obj = "ProtoClassSelect"@ %i;
|
||||
if (%obj.isStateOn())
|
||||
%namespaceUsed = getWord(%this.classlist,%inheritanceOrder);
|
||||
}
|
||||
|
||||
%file = new FileObject();
|
||||
%filename = classPrototyping-->targetPath.text @"/"@ %namespaceUsed @"."@ $TorqueScriptFileExtension;
|
||||
|
||||
if (!isObject(%this.callbacksDefined))
|
||||
%this.callbacksDefined = new arrayobject();
|
||||
%this.callbacksDefined.empty();
|
||||
%this.reportedCommands = false;
|
||||
%this.reportedVariables = false;
|
||||
%this.callbackBlockDefined = false;
|
||||
|
||||
%key=0;
|
||||
if(%file.openForRead(%filename))
|
||||
{
|
||||
while (!%file.isEof())
|
||||
{
|
||||
%line = %file.readLine();
|
||||
|
||||
//have we already reported commands?
|
||||
if (startsWith(%line,"/* Available Commands:") )
|
||||
%this.reportedCommands = true;
|
||||
|
||||
//have we already reported variables?
|
||||
if (startsWith(%line,"/* HardCoded Variables") )
|
||||
%this.reportedVariables = true;
|
||||
|
||||
if (startsWith(%line,"/*--- Callbacks ---*/") )
|
||||
%this.callbackBlockDefined = true;
|
||||
|
||||
//get list of methods already existing
|
||||
if (startswith(%line,"function "@ %namespaceUsed) )
|
||||
{
|
||||
%methodName = strreplace(%line,"::"," ");
|
||||
%methodName = getWord(strreplace(%methodName,"("," "),2);
|
||||
%this.callbacksDefined.add(%key++,%methodName);
|
||||
}
|
||||
}
|
||||
}
|
||||
%file.delete();
|
||||
}
|
||||
|
||||
function classPrototyping::writeResults(%this)
|
||||
{
|
||||
%namespaceUsed = "";
|
||||
for (%i=0; %i<%this.classCount; %i++)
|
||||
{
|
||||
%inheritanceOrder = %this.classCount-(%i+1);
|
||||
%obj = "ProtoClassSelect"@ %i;
|
||||
if (%obj.isStateOn())
|
||||
%namespaceUsed = getWord(%this.classlist,%inheritanceOrder);
|
||||
}
|
||||
%this.readExistingLayout();
|
||||
%file = new FileObject();
|
||||
%filename = classPrototyping-->targetPath.text @"/"@ %namespaceUsed @"."@ $TorqueScriptFileExtension;
|
||||
if(%file.openForAppend(%filename))
|
||||
{
|
||||
if (ReportCommands.isStateOn() && %this.reportedCommands == false)
|
||||
{
|
||||
%this.commandArray = getMethodSigsNS(%this.nameSpaceUsed,true);
|
||||
%this.commandCount = %this.commandArray.count();
|
||||
%file.writeLine("/* Available Commands:");
|
||||
for (%i=0; %i< %this.commandCount; %i++)
|
||||
{
|
||||
%file.writeLine(getRecord(%this.commandArray.getValue(%i),0));
|
||||
}
|
||||
%file.writeLine("*/");
|
||||
}
|
||||
|
||||
if (ReportVariables.isStateOn() && %this.reportedVariables == false)
|
||||
{
|
||||
%file.writeLine("/* HardCoded Variables");
|
||||
for (%i=0; %i< getFieldCountNS(%this.nameSpaceUsed); %i++)
|
||||
{
|
||||
%file.writeLine(getFieldNS(%this.nameSpaceUsed,%i));
|
||||
}
|
||||
%file.writeLine("*/");
|
||||
}
|
||||
|
||||
if (%this.callbackBlockDefined == false)
|
||||
%file.writeLine("\n/*--- Callbacks ---*/\n");
|
||||
|
||||
for (%i=0; %i<%this.methodCount; %i++)
|
||||
{
|
||||
%obj = "ProtoMethodSelect"@ %i;
|
||||
if (%obj.isStateOn())
|
||||
{
|
||||
%methodDef = getRecord(%this.methodArray.getValue(%i),0);
|
||||
%methodName = strreplace(%methodDef,"::"," ");
|
||||
%methodName = getWord(strreplace(%methodName,"("," "),2);
|
||||
if (%this.callbacksDefined.countValue(%methodName)==0)
|
||||
{
|
||||
echo(%methodName @ "not found. defining...");
|
||||
%file.writeLine("\n" @ strreplace(%this.methodArray.getValue(%i),%this.instanceName,%namespaceUsed));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
error( "Failed to open " @ %filename @ " for writing" );
|
||||
}
|
||||
%file.delete();
|
||||
}
|
||||
|
|
@ -2582,12 +2582,24 @@ function GuiEditor::onControlDropped(%this, %payload, %position)
|
|||
//dealing with an actual asset, so build the assetName
|
||||
%assetId = %payload.moduleName @ ":" @ %payload.assetName;
|
||||
%assetType = AssetDatabase.getAssetType(%assetId);
|
||||
|
||||
if(%assetType $= "ImageAsset")
|
||||
|
||||
if(AssetBrowser.isMethod("on" @ %assetType @ "GUIEditorDropped"))
|
||||
{
|
||||
%cmd = "return new guiBitmapCtrl();";
|
||||
%ctrl = eval( %cmd );
|
||||
%ctrl.bitmap = %assetId;
|
||||
%module = %payload.moduleName;
|
||||
%asset = %payload.assetName;
|
||||
%assetDef = AssetDatabase.acquireAsset(%module @ ":" @ %asset);
|
||||
%buildCommand = AssetBrowser @ ".on" @ %assetType @ "GUIEditorDropped(" @ %assetDef @ ",\"" @ %pos @ "\");";
|
||||
eval(%buildCommand);
|
||||
}
|
||||
else
|
||||
{
|
||||
//fallback example
|
||||
if(%assetType $= "ImageAsset")
|
||||
{
|
||||
%cmd = "return new guiBitmapCtrl();";
|
||||
%ctrl = eval( %cmd );
|
||||
%ctrl.bitmap = %assetId;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
|
|
|
|||
|
|
@ -94,7 +94,7 @@ function ImportAssetWindow::onWake(%this)
|
|||
//
|
||||
function isImageFormat(%fileExt)
|
||||
{
|
||||
if( (%fileExt $= ".png") || (%fileExt $= ".jpg") || (%fileExt $= ".bmp") || (%fileExt $= ".dds") || (%fileExt $= ".tif"))
|
||||
if( (%fileExt $= ".png") || (%fileExt $= ".jpg") || (%fileExt $= ".bmp") || (%fileExt $= ".dds") || (%fileExt $= ".tif") || (%fileExt $= ".psd") || (%fileExt $= ".gif") || (%fileExt $= ".hdr"))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
|
|
|
|||
|
|
@ -91,6 +91,17 @@ function AssetBrowser::onSoundAssetEditorDropped(%this, %assetDef, %position)
|
|||
|
||||
}
|
||||
|
||||
function AssetBrowser::onSoundAssetGUIEditorDropped(%this, %assetDef, %position)
|
||||
{
|
||||
%assetId = %assetDef.getAssetId();
|
||||
%cmd = "new GuiAudioCtrl(){";
|
||||
%cmd = %cmd @ "SoundAsset =\""@ %assetId @"\";";
|
||||
%cmd = %cmd @ "position =\""@ %position @"\";";
|
||||
%cmd = %cmd @ "};";
|
||||
%ctrl = GuiEditCanvas.createObject(%cmd);
|
||||
echo(%ctrl SPC "created");
|
||||
}
|
||||
|
||||
function GuiInspectorTypeSoundAssetPtr::onControlDropped( %this, %payload, %position )
|
||||
{
|
||||
Canvas.popDialog(EditorDragAndDropLayer);
|
||||
|
|
|
|||
|
|
@ -211,6 +211,8 @@ function GuiEditCanvas::onCreateMenu(%this)
|
|||
};
|
||||
};
|
||||
|
||||
%this.buildAddMenu();
|
||||
|
||||
// Workaround (for some reason it doesn't size to the width of the canvas)
|
||||
// TODO: After a canvas resize it still messes up the width
|
||||
%position = %this.menubar.position.x SPC %this.menubar.position.y;
|
||||
|
|
@ -220,6 +222,88 @@ function GuiEditCanvas::onCreateMenu(%this)
|
|||
%this.menuBar.attachToCanvas( Canvas, 0 );
|
||||
}
|
||||
|
||||
function GuiEditCanvas::buildAddMenu(%this)
|
||||
{
|
||||
%addMenu = MenuBuilder::newMenu("Add");
|
||||
|
||||
%enumeratedClasses = enumerateConsoleClasses("GuiControl");
|
||||
for(%c=0; %c < getFieldCount(%enumeratedClasses); %c++)
|
||||
{
|
||||
%class = getField(%enumeratedClasses, %c);
|
||||
|
||||
if( GuiEditor.isFilteredClass( %class )
|
||||
|| !isMemberOfClass( %class, "GuiControl" ) )
|
||||
continue;
|
||||
|
||||
%category = getCategoryOfClass(%class);
|
||||
|
||||
if(%category $= "")
|
||||
{
|
||||
error("Attempted to fetch category of class " @ %class @ " but none were found.");
|
||||
continue;
|
||||
}
|
||||
|
||||
%parentMenu = %addMenu; //start at the top
|
||||
for(%cat=0; %cat < getFieldCount(%category); %cat++)
|
||||
{
|
||||
%subCat = getField(%category, %cat);
|
||||
%targetSubmenu = %parentMenu.findMenu(%subCat);
|
||||
if(!isObject(%targetSubmenu))
|
||||
{
|
||||
%targetSubmenu = %parentMenu.newSubmenu(%subCat);
|
||||
}
|
||||
|
||||
%parentMenu = %targetSubmenu;
|
||||
}
|
||||
|
||||
%buildfunc = "";
|
||||
%class = %class;
|
||||
%method = "build" @ %buildfunc;
|
||||
if( !GuiEditCanvas.isMethod( %method ) )
|
||||
%method = "build" @ %class;
|
||||
|
||||
if( !GuiEditCanvas.isMethod( %method ) )
|
||||
%cmd = "return new " @ %class @ "();";
|
||||
else
|
||||
%cmd = "GuiEditCanvas." @ %method @ "();";
|
||||
|
||||
%createCmd = "GuiEditCanvas.newObjectCallback = \"GuiEditCanvas.onFinishCreateObject\"; GuiEditCanvas.createObject( \"" @ %cmd @ "\" );";
|
||||
|
||||
%targetSubmenu.newItem(%class, %createCmd, "");
|
||||
}
|
||||
|
||||
MenuBuilder::addMenuToMenubar(%this.menubar, %addMenu, 4);
|
||||
}
|
||||
|
||||
function GuiEditCanvas::createObject( %this, %cmd )
|
||||
{
|
||||
if(startsWith(%cmd, "return "))
|
||||
%objId = eval(%cmd);
|
||||
else
|
||||
%objId = eval("return " @ %cmd);
|
||||
|
||||
if( isObject( %objId ) )
|
||||
%this.onFinishCreateObject( %objId );
|
||||
|
||||
return %objId;
|
||||
}
|
||||
|
||||
function GuiEditCanvas::onFinishCreateObject( %this, %objId )
|
||||
{
|
||||
GuiEditor.getCurrentAddSet().add( %objId );
|
||||
%this.onObjectCreated( %objId );
|
||||
}
|
||||
|
||||
function GuiEditCanvas::onObjectCreated( %this, %objId )
|
||||
{
|
||||
// Can we submit an undo action?
|
||||
if ( isObject( %objId ) )
|
||||
GuiEditor.onAddNewCtrl( %objId );
|
||||
|
||||
GuiEditorTreeView.clearSelection();
|
||||
GuiEditor.select(%objId);
|
||||
}
|
||||
|
||||
$GUI_EDITOR_MENU_EDGESNAP_INDEX = 0;
|
||||
$GUI_EDITOR_MENU_CENTERSNAP_INDEX = 1;
|
||||
$GUI_EDITOR_MENU_GUIDESNAP_INDEX = 3;
|
||||
|
|
|
|||
|
|
@ -128,6 +128,8 @@ function UndoActionAddDelete::trashObjects(%this)
|
|||
|
||||
if( isObject( %this.tree ) )
|
||||
%this.tree.update();
|
||||
|
||||
GuiEditor.clearSelection();
|
||||
}
|
||||
|
||||
function UndoActionAddDelete::restoreObjects(%this)
|
||||
|
|
|
|||
Loading…
Reference in a new issue