#include "StateObject.h" #include "resourceManager.h" #include "usageDatabase.h" #include "uniqueID.h" #include "SpriteResource.h" #include "imageCache.h" #include "packSaver.h" #include "minorGems/util/stringUtils.h" #include "minorGems/util/log/AppLog.h" static unsigned char objectVersionNumber = 3; // static inits StateObject *StateObject::sBlankObject = NULL; void StateObject::staticInit() { sBlankObject = new StateObject(); // save once sBlankObject->finishEdit(); } void StateObject::staticFree() { delete sBlankObject; } void StateObject::setupDefault() { const char *defaultName = "default"; memcpy( mName, defaultName, strlen( defaultName ) + 1 ); makeUniqueID(); } StateObject::StateObject() { setupDefault(); } void StateObject::initFromData( unsigned char *inData, int inLength, char inFromNetwork ) { readFromBytes( inData, inLength ); if( inFromNetwork ) { // sprites might not be present locally, either // more efficient to load them in a chunk instead of with separate // messages int numSprites = mSpriteLayers.size(); if( numSprites > 0 ) { uniqueID *ids = mSpriteLayers.getElementArray(); int *chunkLengths = new int[ numSprites ]; char *fromNetworkFlags = new char[ numSprites ]; unsigned char **chunks = loadResourceData( "sprite", numSprites, ids, chunkLengths, fromNetworkFlags ); for( int i=0; i displacedIDs; SimpleVector displacedOffsets; SimpleVector displacedTrans; SimpleVector displacedGlow; int oldSize = mSpriteLayers.size(); for( int i=thisIndex; i=thisIndex; i--) { mSpriteLayers.deleteElement( i ); mLayerOffsets.deleteElement( i ); mLayerTrans.deleteElement( i ); mLayerGlow.deleteElement( i ); } // displaced layers removed // add new layer to end mSpriteLayers.push_back( inSpriteID ); intPair p = { 0, 0 }; mLayerOffsets.push_back( p ); mLayerTrans.push_back( 255 ); mLayerGlow.push_back( 0 ); // add displaced after that. int numDisplaced = displacedIDs.size(); for( int i=0; i 10 ) { printf( "Error: StateObject name %s is too long (10 max)\n", inName ); } else { memcpy( mName, inName, newLength + 1 ); } } char *StateObject::getStateObjectName() { return stringDuplicate( mName ); } // finishes the edit, generates a new unique ID, saves result void StateObject::finishEdit( char inGenerateNewID ) { uniqueID oldID = mID; if( inGenerateNewID ) { makeUniqueID(); } if( !equal( oldID, mID ) || ! resourceExists( "object", mID ) ) { // change int numBytes; unsigned char *bytes = makeBytes( &numBytes ); saveResourceData( "object", mID, mName, bytes, numBytes ); delete [] bytes; // track usages int numSprites = mSpriteLayers.size(); for( int j=0; j= 0 && fullX < imageW && fullX >= 0 ) { int index = y * P + x; Color c = spriteImage->getColor( index ); float colorTrans = c.a * layerTrans; if( colorTrans > 0 ) { int fullIndex = fullY * imageW + fullX; Color oldColor = fullImage.getColor( fullIndex ); Color *blend; if( oldColor.a > 0 ) { if( ! layerGlow ) { blend = Color::linearSum( &c, &( oldColor ), colorTrans ); } else { // additive blend = oldColor.copy(); blend->r += c.r * colorTrans; blend->g += c.g * colorTrans; blend->b += c.b * colorTrans; // cap if( blend->r > 1 ) { blend->r = 1; } if( blend->g > 1 ) { blend->g = 1; } if( blend->b > 1 ) { blend->b = 1; } } } else { blend = c.copy(); blend->a = colorTrans; } // overwrite with blend of ink from higher layer fullImage.setColor( fullIndex, *blend ); delete blend; if( fullX < lowX ) { lowX = fullX; } if( fullX > hiX ) { hiX = fullX; } if( fullY < lowY ) { lowY = fullY; } if( fullY > hiY ) { hiY = fullY; } } } } } delete spriteImage; } int subW = hiX - lowX + 1; int subH = hiY - lowY + 1; Image *subImage = fullImage.getSubImage( lowX, lowY, subW, subH ); //printf( "subimage = x,y=(%d,%d), w,h=(%d,%d)\n", // lowX, lowY, subW, subH ); // build up sums here Image *shrunkImage = new Image( P, P, 4, true ); double *subChannels[4]; double *shrunkChannels[4]; int hitCount[P*P]; memset( hitCount, 0, P*P*sizeof(int) ); for( int c=0; c<4; c++ ) { subChannels[c] = subImage->getChannel( c ); shrunkChannels[c] = shrunkImage->getChannel( c ); } double scaleFactor; if( subW > subH ) { scaleFactor = P / (double)subW; } else { scaleFactor = P / (double)subH; } if( scaleFactor > 1 ) { // P big enough to contain whole object scaleFactor = 1; } // center in shrunk image int xExtra = (int)( P - ( scaleFactor * subW ) ); int yExtra = (int)( P - ( scaleFactor * subH ) ); int xOffset = xExtra / 2; int yOffset = yExtra / 2; for( int y=0; y 0 ) { hitCount[shrunkIndex] ++; for( int c=0; c<4; c++ ) { shrunkChannels[c][shrunkIndex] += subChannels[c][subIndex]; } // anything hit by non-trans color is opaque //shrunkChannels[3][shrunkIndex] = 1; } } } for( int c=0; c<4; c++ ) { for( int i=0; i 0 ) { shrunkChannels[c][i] /= hitCount[i]; } } } // sharp transparency already computed above // (skipped trans pixels in sums) delete subImage; cachedImage = shrunkImage; if( inCacheOK ) { addCachedImage( mID, inUseTrans, cachedImage ); } } Sprite *sprite = new Sprite( cachedImage ); if( !inCacheOK ) { // image not in cache... we must destroy it delete cachedImage; } return sprite; } const char *StateObject::getResourceType() { return "object"; } StateObject StateObject::getDefaultResource() { return *sBlankObject; } #include "minorGems/util/SimpleVector.h" unsigned char *StateObject::makeBytes( int *outLength ) { SimpleVector buffer; buffer.push_back( (unsigned char *)SID_MAGIC_CODE, strlen( SID_MAGIC_CODE ) ); buffer.push_back( objectVersionNumber ); unsigned char numLayers = (unsigned char)mSpriteLayers.size(); // one byte for num layers (max 255 layers) buffer.push_back( numLayers ); for( int i=0; ibytes, U ); intPair *p = mLayerOffsets.getElement( i ); // one byte each coordinate buffer.push_back( (unsigned char)p->x ); buffer.push_back( (unsigned char)p->y ); buffer.push_back( *( mLayerTrans.getElement( i ) ) ); buffer.push_back( *( mLayerGlow.getElement( i ) ) ); } buffer.push_back( (unsigned char *)mName, strlen( mName ) + 1 ); *outLength = buffer.size(); return buffer.getElementArray(); } void StateObject::readFromBytes( unsigned char *inBytes, int inLength ) { unsigned char version = 1; // check for magic code before version number const char *code = SID_MAGIC_CODE; int codeLength = strlen( code ); if( inLength >= codeLength ) { char codeFound = true; for( int i=0; i= 2 ) { mLayerTrans.push_back( inBytes[0] ); inBytes = &( inBytes[ 1 ] ); inLength -= 1; if( version >= 3 ) { mLayerGlow.push_back( inBytes[0] ); inBytes = &( inBytes[ 1 ] ); inLength -= 1; } else { // fill in with dummy data mLayerGlow.push_back( 0 ); } } else { // fill in with dummy data mLayerTrans.push_back( 255 ); } } // remainder is name if( inLength <= 11 ) { memcpy( mName, inBytes, inLength ); } } void StateObject::makeUniqueID() { int numBytes; unsigned char *bytes = makeBytes( &numBytes ); mID = ::makeUniqueID( bytes, numBytes ); delete [] bytes; } void StateObject::print() { char *idString = getHumanReadableString( mID ); printf( "StateObject %s:\n", idString ); delete [] idString; for( int i=0; ix, p->y, *(mLayerTrans.getElement( i )), *(mLayerGlow.getElement( i )) ); delete [] idString; } printf( "\n" ); }