1.0 - The Distribution2.0 - Using the Utility
2.1 - Scripts
2.1.1 - Unit Sprite Scripts
2.1.2 - Good Sprite Scripts
2.1.3 - City Sprite Scripts
2.1.4 - Effect Sprite Scripts
2.2 - TIFF Folders and the Source Images3.0 - Graphics Format
4.0 - File Names
4.1 - Graphics File Names
4.2 - Script File Names5.0 - For the Not-So-Faint-Of-Heart
README.html
LEGAL.txt
makespr
units
1
gu01.txt
goods
4
gg04.txt
cities
2
gc002.txt
effects
21
gx21.txt
To use the utility you'll need:
1. A collection of TIFF images for the sprite you want to make
2. A script file describing the sprite, and the images involved.
3. The makespr utility
Depending on what type of sprite you're making, you'll need different image files. See each section below for details on how to make each type of sprite.
Here's the usage for the utility:
makespr 0.3: turns a bunch o' tifs into an '.SPR'
Usage: makespr [-u] [-c] [-g] [-x] [sprite id]
-u Convert a Unit sprite
-c Convert a City sprite
-g Convert a Good sprite
-x Convert an Effect sprite
[id] Must be between 0 and 99.
Copy the makespr program to the directory where you have your script file. The script filename must follow the script file naming convention as detailed below. In this example, it's a Unit Sprite script file. The program, the script file, and the directory full of images must be in the same directory:
The name of the directory with the image files is the id number of the sprite you're making, with any leading zeroes removed. Thus, gu01.txt refers to sprite 01, or 1, the directory must be called "1"units
1
gu01.txt
makespr
From the command line, issue:
makespr -u 1
and a gu01.spr will be generated and placed in the same directory as the script file.
Install the sprite files by placing them in
[CivCTP]/ctp_data/default/graphics/sprites/
The file [CivCTP]/ctp_data/default/gamedata/spriteid.txt contains the mappings from sprite id numbers to the actual sprites.
Scripts are made up of tags and values. Most tags have a corresponding
value, others just signify a block of other tags and values. Blocks
associated with tags have a boolean value for that tag. That is,
0 or 1, indicating whether the block is supposed to be defined in the script.
1. Move - displayed when the unit is moving
2. Attack - displayed when the unit attacks another unit, or
is attacked by another unit
3. Idle - displayed periodically while the unit is standing still
and doing nothing
4. Victory/Death - Displayed when the unit wins/loses a battle
5. Special Action - Displayed for special attacks or things like
launch or re-entry from space.
Every Unit MUST have at least one frame (frame zero) of the Move animation. If you want to make single-frame Unit sprites, this is what you want. You'll define only frame zero of the Move animation for each of the 5 facings, and you'll be in business.
A Unit can face 8 directions:
1
8 | 2
\|/
7---+---3
/|\
6 | 4
5
To save memory and disk space, we removed facings 6, 7, and 8, and simply mirror the 2, 3, and 4 facings horizontally to simulate a full 8 facings of graphics using only 5 total facings. Like this:
1
2* | 2
\|/
3*--+---3
/|\
4* | 4
5
So, to make graphics for a sprite, you only need to create images for facings 1-5, and the other 3 facings will be generated by the code. This lightens the graphics burden a little bit as well. The facings are referred to as 1-5, and they are the directions indicated above. Some unit animations need the full 5 facings (Move, Attack, Special Action) and some only require 1 facing (Idle, Victory/Death.)
Move - requires 5 facings
Attack - requires 5 facings
Idle - requires 1 facing
Victory/Death - requires 1 facing
Special Action - requires 5 facings
Here's a sample Unit Sprite script, commented heavily. It's a script for the Cow sprite, which has a Move and a Death animation only.
##############################################################
# This is a sample Unit Sprite script.
##############################################################0 # this leading zero is required (it can be any number. A number
# here is required, but ignored currently#
# The UNIT_SPRITE keyword indicates that this is a sprite for units
#
UNIT_SPRITE
{
# The UNIT_SPRITE_MOVE tag specifies whether this sprite has
# a Move action. It has a value of either 0 or 1. If it's 0,
# no sprite action definition block is required. Since it's 1,
# this block is required.
#
UNIT_SPRITE_MOVE 1
{
# This is the sprite action definition block. All sprite
# actions require this block if the sprite action is defined.
#
SPRITE_NUM_FRAMES 11 # number of frames of animation
SPRITE_FIRST_FRAME 0 # the index of the first frame
SPRITE_WIDTH 96 # the width of each sprite image
SPRITE_HEIGHT 72 # the height of each sprite image
SPRITE_HOT_POINTS # the hot points (center points) of
# the sprite for each of the 5 facings
49 54 # facing 1
43 51 # facing 2
50 48 # facing 3
58 38 # facing 4
74 53 # facing 5
}# The ANIM block is required for each UNIT_SPRITE_* action block
# that is defined (set to 1, and has a sprite action definition
# block.) It describes the animation that will pay, given the
# sprite frames described in the UNIT_SPRITE_* block.
#
ANIM 1
{
ANIM_TYPE 1 # what type of animation is this?
# 0 = sequential (play through from
# the first frame to the last
# defined frame
# 1 = looped (starts over at the
# first frame when it's gone
# through all of the frames
# 2 = back-n-forth (reverses
# direction of playback when
# it reaches the last frame,
# and playes back to the first
# before reversing direction
# again.
ANIM_NUM_FRAMES 10 # How many frames of animation?
ANIM_PLAYBACK_TIME 1000 # How much time does it take to play
# back the animation (in ms.) This
# is not currently supported.
ANIM_DELAY 0 # amount of time to repeat the very
# last frame of the animation before
# continuing (when playing looped
# or Back 'n Forth animations)
ANIM_FRAME_DATA # the list of the sprite frames to
1 2 3 4 5 # to play (must be the same number
6 7 8 9 10 # as defined in ANIM_NUM_FRAMES)#
# The ANIM_MOVE_DELTAS are nudges applied to each frame of
# animation which bump the draw location the specified offset.
# They allow an single static frame to be moved around a fixed
# location, for instance.
#
# There's one per frame of animation as defined in
# ANIM_NUM_FRAMES
#
ANIM_MOVE_DELTAS 1
{
0 0
0 0
0 0
0 0
0 0
0 0
0 0
0 0
0 0
0 0}
#
# This is a 4 bit transparency value, ranged from 0-15, to be
# applied to each frame. This allows the sprite to fade in or
# out over the course of animation, or whatever transparency
# might be used for. 0 is invisible, 15 is opaque
#
ANIM_TRANSPARENCIES 1
{
15 15 15 15 15
15 15 15 15 15
}}
#
# The Attack animation for this sprite. As you can see, this
# unit doesn't have an Attack animation, so the tag's value is zero.
# Because this tag's value is zero, there's no sprite action
# definition block, and no ANIM {} block associated with this action.
#
UNIT_SPRITE_ATTACK 0#
# The Idle animation for this unit. There's no Idle animation
# either.
#
# If this sprite had an idle animation, the format of the sprite
# action definition block would be exactly the same as the format
# of the Move block above
#
UNIT_SPRITE_IDLE 0#
# This Unit DOES have a victory/death animation. For this sprite
# action definition block, and this one only (Victory) there's
# an additional flag field immediately following the
# UNIT_SPRITE_VICTORY tag, and that's UNIT_SPRITE_IS_DEATH, which
# is set to either 0 (indicating that this animation specifies
# a Victory animation--an animation to play when this unit wins
# a battle) or 1 (indicating that this is a death animation, which
# will play when the unit is killed.)
#
UNIT_SPRITE_VICTORY 1
UNIT_SPRITE_IS_DEATH 1
{
SPRITE_NUM_FRAMES 17
SPRITE_FIRST_FRAME 1
SPRITE_WIDTH 96
SPRITE_HEIGHT 72
SPRITE_HOT_POINT 58 38
}ANIM 1
{
ANIM_TYPE 0
ANIM_NUM_FRAMES 33
ANIM_PLAYBACK_TIME 3300
ANIM_DELAY 0
ANIM_FRAME_DATA
0 1 2 3 4
5 5 5 5 5
6 6 7 7 8
8 9 9 10 10
11 11 12 12 13
13 14 14 15 15
16 16 0ANIM_MOVE_DELTAS 1
{
0 0
0 0
0 0
0 0
0 0
0 0
0 0
0 0
0 0
0 0
0 0
0 0
0 0
0 0
0 0
0 0
0 0
0 0
0 0
0 0
0 0
0 0
0 0
0 0
0 0
0 0
0 0
0 0
0 0
0 0
0 0
0 0
0 0}
ANIM_TRANSPARENCIES 1
{
15 15 15 15 15
15 15 15 15 15
15 15 15 15 15
15 15 15 15 15
15 15 15 15 15
15 15 15 15 15
15 15 15
}}
#
# This is the Unit Sprite's Work animation (Special Animation)
# to be used when doing special attacks or special movement.
#
# As you can see, it's not defined.
#
UNIT_SPRITE_WORK 0#
# Unit Sprite Attributes Section
#
#
# This section specifies a bunch of unit sprite attributes
##
# Firepoints
#
# This block indicates where on the sprite that ammunition should
# originate when the unit attacks. This is currently ignored.
#
UNIT_SPRITE_FIREPOINTS 0# Work Firepoints
#
# This block indicates where on the sprite that the ammunition
# should originate when the unit special attacks. This is also
# currently ignored.
#
UNIT_SPRITE_FIREPOINTS_WORK 0# UNIT_SPRITE_MOVEOFFSETS
#
# These are sprite nudges to be applied when the unit is moving.
# They are an alternate way of specifying how many pixels in each
# direction that unit should move. There's one pair of offsets
# (x,y) for each facing (1-5).
#
# These are not currently used in the engine.
#
UNIT_SPRITE_MOVEOFFSETS 1
{
0 0
0 0
0 0
0 0
0 0
}# UNIT_SPRITE_SHIELDPOINTS
#
# This tag's block, when defined, allows you specify where the
# unit's herald shield will appear in the sprite for each of the
# 5 facings, for each of the 5 actions that the unit will perform.
#
# Often a fixed shield position wouldn't always work, as the shield
# might cover up an important part of the sprite. This block
# allows specific points to be used.
#
UNIT_SPRITE_SHIELDPOINTS 1
{
UNIT_SPRITE_SHIELDPOINTS_MOVE
82 9
82 9
82 9
82 9
82 9
UNIT_SPRITE_SHIELDPOINTS_ATTACK
48 36
48 36
48 36
48 36
48 36
UNIT_SPRITE_SHIELDPOINTS_IDLE
48 36
48 36
48 36
48 36
48 36
UNIT_SPRITE_SHIELDPOINTS_VICTORY
82 9
48 36
48 36
48 36
48 36
UNIT_SPRITE_SHIELDPOINTS_WORK
48 36
48 36
48 36
48 36
48 36
}}
##############################################################
# Sample Good Sprite Script
##############################################################
#0 # Required 0 here
#
# The TAG for this sprite is GOOD_SPRITE
#GOOD_SPRITE
{
#
# Goods only have one Action, and that's Idle.
#
# This is a single-facing sprite, not a multiple facing one.
#
GOOD_SPRITE_IDLE 1
{
SPRITE_NUM_FRAMES 16
SPRITE_FIRST_FRAME 1
SPRITE_WIDTH 96
SPRITE_HEIGHT 72
SPRITE_HOT_POINT 53 47
}#
# Every Action must have an ANIM block
#
ANIM 1
{
ANIM_TYPE 1
ANIM_NUM_FRAMES 32
ANIM_PLAYBACK_TIME 6400
ANIM_DELAY 5000
ANIM_FRAME_DATA
0 1 2 3 2
3 2 3 2 3
4 5 6 7 8
7 8 7 7 7
8 7 8 7 8
9 10 11 12 13
14 15#
# This block is expanded for illustration purposes
# it could just as easily be replaced with:
# ANIM_MOVE_DELTAS 0
# to achieve the same effect
#ANIM_MOVE_DELTAS 1
{
0 0
0 0
0 0
0 0
0 0
0 0
0 0
0 0
0 0
0 0
0 0
0 0
0 0
0 0
0 0
0 0
0 0
0 0
0 0
0 0
0 0
0 0
0 0
0 0
0 0
0 0
0 0
0 0
0 0
0 0
0 0
0 0}
ANIM_TRANSPARENCIES 1
{
15 15 15 15 15
15 15 15 15 15
15 15 15 15 15
15 15 15 15 15
15 15 15 15 15
15 15 15 15 15
15 15
}
}
}
##############################################################
# This is a sample City Sprite script.
##############################################################
0 # Required 0
#
# For reasons that don't bear explanation, a city is the same
# thing as a unit. City sprites, however, aren't used like units
# in the game engine. It's assumed that city sprites (gc*.spr)
# have only an IDLE action, and nothing else.
#
# So a city sprite's script looks just like a unit sprite's
# script, except that the only action defined is Idle. Here's
# an example:
#UNIT_SPRITE
{
UNIT_SPRITE_MOVE 0UNIT_SPRITE_ATTACK 0
UNIT_SPRITE_IDLE 1
{
SPRITE_NUM_FRAMES 1
SPRITE_FIRST_FRAME 1
SPRITE_WIDTH 96
SPRITE_HEIGHT 76
SPRITE_HOT_POINT 49 54
}ANIM 1
{
ANIM_TYPE 1
ANIM_NUM_FRAMES 1
ANIM_PLAYBACK_TIME 1000
ANIM_DELAY 10000
ANIM_FRAME_DATA
0ANIM_MOVE_DELTAS 0
ANIM_TRANSPARENCIES 0}
UNIT_SPRITE_VICTORY 0
UNIT_SPRITE_WORK 0
UNIT_SPRITE_FIREPOINTS 0
UNIT_SPRITE_FIREPOINTS_WORK 0
UNIT_SPRITE_MOVEOFFSETS 0
UNIT_SPRITE_SHIELDPOINTS 0
}
##############################################################
# This is a sample Effect Sprite script.
##############################################################21 # a number is required here. It can be any number.
#
# An Effect Sprite is much like a good sprite, except that the
# tag EFFECT_SPRITE implies that the script will define
# EFFECT_SPRITE_PLAY and EFFECT_SPRITE_FLASH. The sprite must
# have an EFFECT_SPRITE_PLAY block, and may optionally have
# an EFFECT_SPRITE_FLASH block.
#
# The *_PLAY block defines the animation for the effect, and
# the *_FLASH block defines a special effect flash that can
# be drawn in the frame as well. The graphics in the *_FLASH
# block are drawn in an additive mode, rather than a standard
# pixel copy mode, to allow for some lighting effects.
#
# The *_FLASH was originally designed to let us light up the
# terrain around the sprite being drawn.
#EFFECT_SPRITE
{
#
# The Play block is required, and looks much like a normal
# sprite definition block (See the Unit Sprite Script's
# Idle block.)
#
# Note that an ANIM block is required whenever a sprite
# action block is defined.
#
EFFECT_SPRITE_PLAY 1
{
SPRITE_NUM_FRAMES 12
SPRITE_FIRST_FRAME 1
SPRITE_WIDTH 96
SPRITE_HEIGHT 72
SPRITE_HOT_POINT
58 58 # facing 1
}ANIM 1
{
ANIM_TYPE 0 # 0 = Seq 1 = looped 2 = bnf
ANIM_NUM_FRAMES 24
ANIM_PLAYBACK_TIME 4800 # 1 second
ANIM_DELAY 0 # no delay
ANIM_FRAME_DATA
0 1 2 3 4
5 6 7 8 9
10 11 0 1 2
3 4 5 6 7
8 9 10 11
ANIM_MOVE_DELTAS 0
ANIM_TRANSPARENCIES 0
}#
# Flash is not currently defined for this sprite, but if it were,
# it would take the form of the EFFECT_SPRITE_PLAY block exactly.
#
EFFECT_SPRITE_FLASH 0
}
See Section 4.0 of this document for the naming convention for sprite
source images. This naming convention must be observed religiously,
or the makespr program won't function.
Here's an example. These are all from the same 32 bit TIFF file, gu01ma3.3.tif:
Here's the RGB image of the cow:
Here's the alpha channel for that image. Black (0) means no alpha, and white (255) means full alpha. Gray levels in between apply a fractional alpha.
Also associated with this TIFF is a shadow file, which contains information about the shadow that this sprite casts. The shadow file is optional. makespr will look for a shadow file that corresponds to each 32 bit image TIFF that it loads, and it'll use it if it finds it. Otherwise, shadows are ignored.
The shadow is in a separate file, in this case, gu01ms3.3.tif:
The shadow image is a 24 bit TIFF (no alpha channel). makespr
looks for black pixels or white pixels. White pixels mean no shadow,
black pixels mean shadow. This image is 1 bit (black or white, no
gray.)
All of these images must be of the size 96x72 pixels.
As makespr processes each sprite, it takes the information in the 32 bit (ARGB) TIFF and the 24 bit (RGB) shadow file (if it exists) and combines them to make each frame of the sprite.
Units:
gu 01 m a 1 . 0 . tif Type (Graphics, Units) Number (must be between 00 and 99) Action:
m - move
a - attack
i - idle
v-victory/death
w-specialAttackAnim or Shadow
(a or s)Facing (1-5 Frame #
(0-99)Must be TIF Move:gu01ma1.0.tif
gu01ms1.0.tif (optional)Attack:
gu01aa1.0.tif
gu01as1.0.tif (optional)Idle:
gu01ia1.0.tif
gu01is1.0.tif (optional)Victory/Death:
gu01va1.0.tif
gu01vs1.0.tif (optional)Special Action:
gu01wa1.0.tif
gu01ws1.0.tif (optional)Goods:
gg 04 a . 1 . tif Type (Graphics, Goods) Number (must be between 00 and 99) Anim or Shadow (a or s) Frame # (1-99) Must be TIF gg04a.1.tif
gg04s.1.tif (optional)Cities:
gc 002 a . 1 . tif Type (Graphics, Cities) Number (must be between 000 and 999) Anim or Shadow (a or s) Frame # (1-99) Must be TIF gc002a.1.tif
gc002s.1.tif (optional)Effects:
gx 21 e a . 1 . tif Type (Graphics, fX) Number (must be between 00 and 99) Effect or Flash (e or f) Anim or Shadow (a or s) Frame # (1-99) Must be TIF gx21ea.1.tif
gx21es.1.tif (optional)gx21fa.1.tif (optional)
Units
gu01.txt - where the number 01 is valid between 00 and 99.Goods
gg01.txt - where the number 01 is valid between 00 and 99.Cities
gc001.txt - where the number 001 is valid between 000 and 999.Effects
gx01.txt - where the number 01 is valid between 00 and 99.
This section describes the data format of the encoded sprites.
This information can be used to create code to interpret, draw, or export
sprites in the hands of someone who knows C/C++.
Activision provides no guarantees, and will not be held responsible for any damages that result from the use of this stuff. It won't compile as it is right here, and is provided only as a framework to work from for those interested in writing code to handle the Civ:CTP sprite format.
Activision also won't provide any sort of tech support for questions regarding this stuff. You're on your own.
Caveat Emptor. Have fun.
#pragma once
#ifndef __SPRITEUTILS_H__
#define __SPRITEUTILS_H__// 16 bit pixels of interest
#define k_CHROMAKEY_PIXEL 0x0000
#define k_SHADOW_PIXEL_565 0xF81F
#define k_SHADOW_PIXEL_555 0x7C1F
#define k_SHADOWBACKGD_PIXEL 0xFFFF
// IDs in the RLE sprite stream
#define k_CHROMAKEY_RUN_ID 0x0A
#define k_COPY_RUN_ID 0x0C
#define k_SHADOW_RUN_ID 0x0E
#define k_FEATHERED_RUN_ID 0x0F// EOLN mask for tag
#define k_EOLN_ID 0xF0// Alpha values
#define k_NO_ALPHA 0x00
#define k_ALL_ALPHA 0xFF// Flag for empty row in sprite table
#define k_EMPTY_TABLE_ENTRY 0xFFFFvoid spriteutils_MergeShadowMap(Pixel32 *buf, Pixel32 *shadowBuf, uint16 width, uint16 height);
// Sprite Encoding
void spriteutils_EncodeShadowRun(Pixel32 **inBuf, sint32 *pos, sint32 width, Pixel16 **outBufPtr);
void spriteutils_EncodeCopyRun(Pixel32 **inBuf, sint32 *pos, sint32 width, Pixel16 **outBufPtr);
char spriteutils_EncodeChromakeyRun(Pixel32 **inBuf, sint32 *pos, sint32 width, Pixel16 **outBufPtr);
void spriteutils_EncodeFeatheredRun(Pixel32 **inBuf, sint32 *pos, sint32 width, Pixel16 **outBufPtr);
char spriteutils_EncodeScanline(Pixel32 *scanline, sint32 width, Pixel16 **outBufPtr);
Pixel16 *spriteutils_RGB32ToEncoded(Pixel32 *buf, Pixel32 *shadowBuf, uint16 width, uint16 height);
Pixel16 *spriteutils_RGB32ToEncoded(Pixel32 *buf, uint16 width, uint16 height);#endif//__SPRITEUTILS_H__
//////////////////////////////////////////////////////////////////////////////
void spriteutils_EncodeShadowRun(Pixel32 **inBuf, sint32 *pos, sint32 width, Pixel16 **outBufPtr)
{
Pixel16 pix16;
uint8 alpha;
sint32 runLen = 0;
Pixel16 footer=0;
Pixel16 shadowPixel;if (g_is565Format) shadowPixel = k_SHADOW_PIXEL_565;
else shadowPixel = k_SHADOW_PIXEL_555;RGB32Info(**inBuf, &pix16, &alpha);
while (pix16 == shadowPixel && (*pos < width)) {
// Increment input buffer
(*inBuf)++;// Increment position counter
(*pos)++;// Increment run length
runLen++;RGB32Info(**inBuf, &pix16, &alpha);
}// Build header/footer ID
footer = (Pixel16)(k_SHADOW_RUN_ID << 8 | runLen);// Set the high nybble of the tag if it's an EOLN condition
if (*pos >= width) footer |= k_EOLN_ID << 8;
// Append header/footer ID to output buffer
**outBufPtr = footer;
(*outBufPtr)++;
}//////////////////////////////////////////////////////////////////////////////
void spriteutils_EncodeCopyRun(Pixel32 **inBuf, sint32 *pos, sint32 width, Pixel16 **outBufPtr)
{
Pixel16 pix16;
uint8 alpha;
sint32 runLen = 0;
Pixel16 footer=0;
Pixel16 *headerPtr;
Pixel16 shadowPixel;if (g_is565Format) {
shadowPixel = k_SHADOW_PIXEL_565;
} else {
shadowPixel = k_SHADOW_PIXEL_555;
}headerPtr = *outBufPtr;
(*outBufPtr)++;RGB32Info(**inBuf, &pix16, &alpha);
while (pix16 != shadowPixel && alpha == k_ALL_ALPHA && (*pos < width)) {// Filter pure-black
if (pix16 == 0x0000) pix16 = 0x0001;// Write pixel to output buffer
**outBufPtr = pix16;// Increment output buffer
(*outBufPtr)++;
// Increment input buffer
(*inBuf)++;// Increment position counter
(*pos)++;// Increment run length
runLen++;RGB32Info(**inBuf, &pix16, &alpha);
}
// Build the tag with the ID and length
footer = (Pixel16) (k_COPY_RUN_ID << 8 | runLen);// Set the high nybble of the tag if it's an EOLN condition
if (*pos >= width) footer |= k_EOLN_ID << 8;*headerPtr = footer;
}//////////////////////////////////////////////////////////////////////////////
char spriteutils_EncodeChromakeyRun(Pixel32 **inBuf, sint32 *pos, sint32 width, Pixel16 **outBufPtr)
{
Pixel16 pix16;
uint8 alpha;
sint32 runLen = 0;
Pixel16 footer=0;RGB32Info(**inBuf, &pix16, &alpha);
while (pix16 == k_CHROMAKEY_PIXEL && alpha == k_NO_ALPHA && (*pos < width)) {
// Increment input buffer
(*inBuf)++;// Increment position counter
(*pos)++;// Increment run length
runLen++;RGB32Info(**inBuf, &pix16, &alpha);
}// If it's a full-row transparent run, it's a skipped row
if (runLen < width) {
// Build header/footer ID
footer = (Pixel16)(k_CHROMAKEY_RUN_ID << 8 | runLen);// Set the high nybble of the tag if it's an EOLN condition
if (*pos >= width) footer |= k_EOLN_ID << 8;
// Append header/footer ID to output buffer
**outBufPtr = footer;
(*outBufPtr)++;
} else {
// return TRUE that it's an empty row
return TRUE;
}return FALSE;
}//////////////////////////////////////////////////////////////////////////////
char spriteutils_EncodeChromakeyWshadowRun(Pixel32 **inBuf, sint32 *pos, sint32 width, Pixel16 **outBufPtr)
{
Pixel16 pix16;
uint8 alpha;
sint32 runLen = 0;
Pixel16 footer=0;RGB32Info(**inBuf, &pix16, &alpha);
while ( ((pix16 == k_CHROMAKEY_PIXEL && alpha == k_NO_ALPHA) || (pix16 == k_SHADOWBACKGD_PIXEL)) && (*pos < width) ) {
// Increment input buffer
(*inBuf)++;// Increment position counter
(*pos)++;// Increment run length
runLen++;RGB32Info(**inBuf, &pix16, &alpha);
}// If it's a full-row transparent run, it's a skipped row
if (runLen < width) {
// Build header/footer ID
footer = (Pixel16)(k_CHROMAKEY_RUN_ID << 8 | runLen);// Set the high nybble of the tag if it's an EOLN condition
if (*pos >= width) footer |= k_EOLN_ID << 8;
// Append header/footer ID to output buffer
**outBufPtr = footer;
(*outBufPtr)++;
} else {
// return TRUE that it's an empty row
return TRUE;
}return FALSE;
}//////////////////////////////////////////////////////////////////////////////
void spriteutils_EncodeFeatheredRun(Pixel32 **inBuf, sint32 *pos, sint32 width, Pixel16 **outBufPtr)
{
Pixel16 pix16;
Pixel16 footer=0;
uint8 alpha;RGB32Info(**inBuf, &pix16, &alpha);
(*inBuf)++;
(*pos)++;footer = (k_FEATHERED_RUN_ID << 8) | alpha;
// Set the high nybble of the tag if it's an EOLN condition
if (*pos >= width)
footer |= k_EOLN_ID << 8;// Write leading footer tag
**outBufPtr = footer;
(*outBufPtr)++;// Write pixel value
**outBufPtr = pix16;
(*outBufPtr)++;}
//////////////////////////////////////////////////////////////////////////////
// this will encode a normal image with shadow merged into it.
char spriteutils_EncodeScanline(Pixel32 *scanline, sint32 width, Pixel16 **outBufPtr)
{
Pixel16 pix16;
Pixel32 pix32;
Pixel32 *scanPtr = scanline;
uint8 alpha;
sint32 pos;
Pixel16 *startPtr;
BOOL empty;
Pixel16 shadowPixel;if (g_is565Format) {
shadowPixel = k_SHADOW_PIXEL_565;
} else {
shadowPixel = k_SHADOW_PIXEL_555;
}
pos = 0;
// Save starting position of the scanline's data stream
startPtr = *outBufPtr;// EOLN needs to be at both ends
while (scanPtr < (scanline + width)) {
pix32 = *scanPtr;
empty = FALSE;RGB32Info(pix32, &pix16, &alpha);
if (pix16 == k_CHROMAKEY_PIXEL && alpha == k_NO_ALPHA)
{
// Whitespace Run
empty = spriteutils_EncodeChromakeyRun(&scanPtr, &pos, width, outBufPtr);
}
else
if (pix16 == shadowPixel) {
// Shadow Run
spriteutils_EncodeShadowRun(&scanPtr, &pos, width, outBufPtr);
}
else
if (alpha != k_NO_ALPHA && alpha != k_ALL_ALPHA) {
// Feathered pixel
spriteutils_EncodeFeatheredRun(&scanPtr, &pos, width, outBufPtr);
}
else {
// Jus' a plain ol' pixel. This is a copy run.
if (alpha == k_ALL_ALPHA)
spriteutils_EncodeCopyRun(&scanPtr, &pos, width, outBufPtr);
else {
printf("\nError in bitmap data. Pixel with no associated alpha.\n");
exit(-1);
}
}
}return empty;
}//////////////////////////////////////////////////////////////////////////////
// this will encode a shadow just like a regular image, taking into account shadow's with white backgrounds
char spriteutils_EncodeScanlineWshadow(Pixel32 *scanline, sint32 width, Pixel16 **outBufPtr)
{
Pixel16 pix16;
Pixel32 pix32;
Pixel32 *scanPtr = scanline;
uint8 alpha;
sint32 pos;
Pixel16 *startPtr;
BOOL empty;
Pixel16 shadowPixel;if (g_is565Format) {
shadowPixel = k_SHADOW_PIXEL_565;
} else {
shadowPixel = k_SHADOW_PIXEL_555;
}
pos = 0;
// Save starting position of the scanline's data stream
startPtr = *outBufPtr;// EOLN needs to be at both ends
while (scanPtr < (scanline + width)) {
pix32 = *scanPtr;
empty = FALSE;RGB32Info(pix32, &pix16, &alpha);
if ( (pix16 == k_CHROMAKEY_PIXEL && alpha == k_NO_ALPHA) || (pix16 == k_SHADOWBACKGD_PIXEL) )
{
// Whitespace Run
empty = spriteutils_EncodeChromakeyWshadowRun(&scanPtr, &pos, width, outBufPtr);
}
else
if (pix16 == shadowPixel) {
// Shadow Run
spriteutils_EncodeShadowRun(&scanPtr, &pos, width, outBufPtr);
}
else
if (alpha != k_NO_ALPHA && alpha != k_ALL_ALPHA) {
// Feathered pixel
spriteutils_EncodeFeatheredRun(&scanPtr, &pos, width, outBufPtr);
}
else {
// Jus' a plain ol' pixel. This is a copy run.
if (alpha == k_ALL_ALPHA)
spriteutils_EncodeCopyRun(&scanPtr, &pos, width, outBufPtr);
else {
printf("\nError in bitmap data. Pixel with no associated alpha.\n");
exit(-1);
}
}
}return empty;
}//////////////////////////////////////////////////////////////////////////////
void spriteutils_MergeShadowMap(Pixel32 *buf, Pixel32 *shadowBuf, uint16 width, uint16 height)
{
Pixel32 *pixPtr, pix;
Pixel32 *shadowPixPtr, shadowPix;// if the upper-left pixel is pure white, assume this is a white background with a black shadow on it
// otherwise, it's a black background with a pure-magenta shadow on it
BOOL whiteBackground = FALSE;
if ((*shadowBuf & 0x00FFFFFF) == 0x00FFFFFF) {
whiteBackground = TRUE;
} else {
if ((*shadowBuf & 0x00FFFFFF) != 0x00000000) {
printf("\nShadow file is in invalid format.\n");
exit(-1);
}
}for (sint32 j=0; j<height; j++) {
for (sint32 i=0; i<width; i++) {
pixPtr = buf + j*width + i;
shadowPixPtr = shadowBuf + j*width + i;pix = *pixPtr;
shadowPix = *shadowPixPtr;
shadowPix = shadowPix & 0x00FFFFFF;if (whiteBackground) {
// See if there's shadow information there
if (shadowPix != 0x00FFFFFF) {
// There's a shadow pixel, copy it over to the source image's bitmap
// only if there's null alpha
Pixel16 r, g, b, a;RGB32Components(pix, &r, &g, &b, &a);
// check against empty and full alpha
if (a != 0xFF) {
*pixPtr = 0x00FF00FF;
}
}
} else {
// See if there's shadow information there
if (shadowPix) {
// There's a shadow pixel, copy it over to the source image's bitmap
// only if there's null alpha
Pixel16 r, g, b, a;RGB32Components(pix, &r, &g, &b, &a);
// check against empty and full alpha
if (a != 0xFF) {
*pixPtr = shadowPix;
}
}
}
}
}
}//////////////////////////////////////////////////////////////////////////////
// this takes an image and a shadow and makes one image with them
Pixel16 *spriteutils_RGB32ToEncoded(Pixel32 *buf, Pixel32 *shadowBuf, uint16 width, uint16 height)
{
Pixel32 *srcPixel = buf;
Pixel16 *outBuf = new Pixel16[(1+height+width*height)*8];
Pixel16 *returnBuf = NULL;
uint16 *table = (uint16 *)outBuf;
Pixel16 *startOfData;
Pixel16 *dataPtr, *startDataPtr;
BOOL empty;
// One solution to the shadow encoding problem is to merge the shadow information sint32o the source bitmap
if (shadowBuf != NULL)
spriteutils_MergeShadowMap(buf, shadowBuf, width, height);// Write the height of the sprite as the first 16 bits of the table data
*table++ = (uint16)height;startOfData = outBuf + 1 + height;
dataPtr = startOfData;for(sint32 y=0; y<height; y++) {
// Start at the beginning of the scanline
srcPixel = buf + width * y;startDataPtr = dataPtr;
// Encode runs for this line
empty = spriteutils_EncodeScanline(srcPixel, width, &dataPtr);
if (empty) {
*table++ = k_EMPTY_TABLE_ENTRY;
} else {
// Write the line start sint32o the table
*table++ = startDataPtr - startOfData;
}
}sint32 resultSize = (dataPtr - outBuf);
returnBuf = new Pixel16[resultSize];
memcpy(returnBuf, outBuf, resultSize * sizeof(Pixel16));
return (Pixel16 *)returnBuf;
}//////////////////////////////////////////////////////////////////////////////
// this takes any buffer and encodes it as it's own image; Including shadows treated as seperate images
Pixel16 *spriteutils_RGB32ToEncoded(Pixel32 *buf, uint16 width, uint16 height)
{
Pixel32 *srcPixel = buf;
Pixel16 *outBuf = new Pixel16[(1+height+width*height)*8];
Pixel16 *returnBuf = NULL;
uint16 *table = (uint16 *)outBuf;
Pixel16 *startOfData;
Pixel16 *dataPtr, *startDataPtr;
BOOL empty;
// Write the height of the sprite as the first 16 bits of the table data
*table++ = (uint16)height;startOfData = outBuf + 1 + height;
dataPtr = startOfData;for(sint32 y=0; y<height; y++) {
// Start at the beginning of the scanline
srcPixel = buf + width * y;startDataPtr = dataPtr;
// Encode runs for this line
empty = spriteutils_EncodeScanlineWshadow(srcPixel, width, &dataPtr);
if (empty) {
*table++ = k_EMPTY_TABLE_ENTRY;
} else {
// Write the line start sint32o the table
*table++ = startDataPtr - startOfData;
}
}sint32 resultSize = (dataPtr - outBuf);
returnBuf = new Pixel16[resultSize];
memcpy(returnBuf, outBuf, resultSize * sizeof(Pixel16));
return (Pixel16 *)returnBuf;
}//////////////////////////////////////////////////////////////////////////////
void spriteutils_CreateQuarterSize(Pixel32 *srcBuf, sint32 srcWidth, sint32 srcHeight, Pixel32 **destBuf, BOOL aa)
{
sint32 destWidth = srcWidth / 2;
sint32 destHeight = srcHeight / 2;
sint32 i,j;
Pixel32 pixel, pixel1, pixel2, pixel3, pixel4;
Pixel32 *outBuf;outBuf = (Pixel32 *)malloc(destWidth * destHeight * sizeof(Pixel32) );
for (i=0; i<destHeight; i++) {
for (j=0; j<destWidth; j++) {
pixel1 = srcBuf[(i * 2) * srcWidth + (j * 2)];if (aa) {
pixel2 = srcBuf[(i * 2) * srcWidth + (j * 2) + 1];
pixel3 = srcBuf[((i * 2) + 1) * srcWidth + (j * 2)];
pixel4 = srcBuf[((i * 2) + 1) * srcWidth + (j * 2) + 1];pixel = spriteutils_AveragePixel32(pixel1, pixel2, pixel3, pixel4);
} else {
pixel = pixel1;
}outBuf[i*destWidth + j] = pixel;
}
}
*destBuf = outBuf;
}