Would you want my c++ wrappers? :-)

Hi,

I’ve been coding a game using SDL in C++ lately, and just like
probably everyone else, I’ve hacked up some C++ SDL wrappers. However,
what I’ve produced aren’t strictly wrappers, there’s some
"value-added" functionality like

  • arbitrary affine transformations (stretching, rotation, shearing)
    on surfaces
  • graphics primitives (polygon, line, box, circle, ellipse)
    filled and non-filled
  • tiling
  • an audio manager (i.e. you can add some streams to that manager,
    and it will take care of samplerate conversion and mixing for you)
  • bitmap fonts (may be prerendered from ttfs)
  • png loading

My wrappers depend on ixlib, which is a generic LGPL tools library.
I’ve included the relevant header files as attachments, FYI. Please
note, this isn’t just a header proposal, there’s actual tested code
behind all this.

I’d like to know whether there’s interest in such wrapper classes and
whether it would be a valuable job to separate this code out into a
library for public consumption, taking into account that we already
have SDL–. (http://sdlmm.sf.net/)

Andreas
-------------- next part --------------
// ----------------------------------------------------------------------------
// Description : SDL video wrappers
// ----------------------------------------------------------------------------
// Remarks : none.
//
// ----------------------------------------------------------------------------
// © Copyright 1999 by Team WAM
// ----------------------------------------------------------------------------

#ifndef WAM_SDL_VIDEO
#define WAM_SDL_VIDEO

#include
#include
#include <ixlib_xml.hh>
#include <ixlib_geometry.hh>
#include <ixlib_polygon.hh>
#include “output/sdl_base.hh”

// wSurface -------------------------------------------------------------------
class wFont;

struct wAffineTransformation {
double Matrix[2][2];
double Translation[2];

void identity();
void translate(double x,double y);
void scale(double x,double y);
void rotate(double rad);
void invert();

void transform(double &dest_x,double &dest_y,double x,double y) const;
void transformLinear(double &dest_x,double &dest_y,double x,double y) const;
};

class wSurface {
public:
enum TTextAlignment {
ALIGN_TOP,ALIGN_BASE,ALIGN_BOTTOM
};
enum TDrawMode {
COLOR,
TILE,
IMAGE,
};

typedef Uint32		TColor;
typedef Sint32		TCoordinate;

SDL_Surface 		*Surface;

protected:
// printing text
TCoordinate CursorX;
TCoordinate CursorY;
TDelta LineDistance;
TTextAlignment TextAlignment;

bool		FreeMem;

TCoordinate		ReferencePointX;
TCoordinate		ReferencePointY;

TDrawMode		DrawMode;
TColor		DrawColor;
const wSurface	*DrawTile,*PixelImage;
TCoordinate		TileZeroX,TileZeroY;

public:
wSurface();
wSurface(SDL_PixelFormat const &fmt,TSize width,TSize height,
Uint32 flags = SDL_SWSURFACE);
wSurface(wSurface const &src,double stretchx = 1,double stretchy = 1,double rotate = 0);
wSurface(wSurface const &src,wAffineTransformation const &tx);
wSurface(SDL_Surface *surface, bool freemem = false);
~wSurface();

void setSurface(SDL_Surface *surface, bool freemem = false);
void load(istream &datastrm);
void save(ostream &datastrm);

TColor mapRGBA(Uint8 r,Uint8 g,Uint8 b,Uint8 a = SDL_ALPHA_OPAQUE);
void unmapRGBA(TColor color,Uint8 &r,Uint8 &g,Uint8 &b,Uint8 &a);
  
TSize getHeight() const {
  return Surface->h;
  }
TSize getWidth() const {
  return Surface->w;
  }
Uint32 getFlags() const {
  return Surface->flags;
  }

TCoordinate getReferencePointX() const {
  return ReferencePointX;
  }
TCoordinate getReferencePointY() const {
  return ReferencePointY;
  }

rectangle<wSurface::TCoordinate> getExtent() const;

bool hasColorKey() const;
TColor getColorKey() const;
void setColorKey(TColor key,Uint32 flags = SDL_SRCCOLORKEY);
void clearColorKey();

bool hasAlphaChannel() const;
bool hasSurfaceAlpha() const;
void setAlphaFlags(Uint32 flags = SDL_SRCALPHA,Uint8 alpha = SDL_ALPHA_OPAQUE);
void ignoreAlpha();

SDL_Rect const &getClipping() {
  return Surface->clip_rect;
  }
void setClipping(SDL_Rect &rect);
void clearClipping();
SDL_PixelFormat const &getDisplayFormat() const;
wSurface *convert(SDL_PixelFormat const &fmt,Uint32 flags) const;
wSurface *convertToDisplay() const;
wSurface *convertForAcceleratedBlit() const;

// low-level
TColor getDrawColor() const;
void setDrawColor(TColor color);
wSurface const *getDrawTile() const;
void setDrawTile(wSurface const *tile,TCoordinate zerox = 0,TCoordinate zeroy = 0);

wSurface const *getPixelImage() const;
void setPixelImage(wSurface const *pixel);

TColor getPixel(TCoordinate x,TCoordinate y);
void setPixel(TCoordinate x,TCoordinate y);
void drawHLine(TCoordinate x1,TCoordinate y,TCoordinate x2);
void drawVLine(TCoordinate x,TCoordinate y1,TCoordinate y2);

// high-level
void drawLine(TCoordinate x1,TCoordinate y1,TCoordinate x2,TCoordinate y2);
void drawBox(TCoordinate x1,TCoordinate y1,TCoordinate x2,TCoordinate y2);
void fillBox(TCoordinate x1,TCoordinate y1,TCoordinate x2,TCoordinate y2);
void drawCircle(TCoordinate x,TCoordinate y,TCoordinate r);
void fillCircle(TCoordinate x,TCoordinate y,TCoordinate r);
void drawEllipse(TCoordinate x,TCoordinate y,TCoordinate r_x,TCoordinate r_y);
void fillEllipse(TCoordinate x,TCoordinate y,TCoordinate r_x,TCoordinate r_y);
void drawPolygon(polygon<int> const &poly);
void fillPolygon(polygon<int> const &poly);

// bitmap output
void putSurface(TCoordinate x,TCoordinate y,wSurface const &pic);

// function for text output
int cursorX() const
  { return CursorX; }
int cursorY() const
  { return CursorY; }
void gotoXY(TCoordinate x,TCoordinate y);
void putChar(TCoordinate x,TCoordinate y, unsigned char c,wFont &font);
void print(string const &text, wFont &font);
void printLn(string const &text, wFont &font);

void setTextAlignment(TTextAlignment align) {
  TextAlignment = align;
  }

static void makePixelFormat(SDL_PixelFormat &fmt,TSize bitdepth,TSize channels = 3,
  TColor colorkey = 0,float surfalpha = 0,SDL_Palette *pal = NULL);

private:
void killSurface();
void putSurface(int const x, int const y, SDL_Rect &srcrect, wSurface &pic);
void copyDataBlock(wSurface const &src);
void copyTransformedSurface(wSurface const &src,wAffineTransformation const &trans);
static void stretchSurface(SDL_Surface *dest,SDL_Surface *src,double stretchx = 1,double stretchy = 1);
static void transformSurface(SDL_Surface *dest,SDL_Surface *src,wAffineTransformation const &trans,TColor back_color = 0);
SDL_Surface *createSameFormatSurface(TSize width,TSize height) const;
};

class wScreenSurface: public wSurface {
public:
wScreenSurface(TSize width,TSize height,Uint32 flags,TSize bpp = 0);

void updateRect(TCoordinate x,TCoordinate y,TCoordinate w,TCoordinate h) {
  SDL_UpdateRect(Surface,x,y,w,h);
  }
void flip() const {
  SDL_Flip(Surface);
  }
void setCaption(string const &caption) {
  SDL_WM_SetCaption(caption.c_str(),NULL);
  }

};

// wFont ----------------------------------------------------------------------
class wFont {
public:
struct wGlyph {
bool Present;
int BaseX;
int BaseY;
unsigned Width;
SDL_Rect Boundary;
};

TSize static const 	MaxCharacterCount = 256;

string             	Name;

// it is implied that the base line is at 0
int                 TopLine;
int                 BottomLine;
wGlyph              CharTable[MaxCharacterCount];
auto_ptr<wSurface>  Surface;

wFont();

rectangle<wSurface::TCoordinate> getExtent(string const &s) const;

void load(xml_file::tag &cfgtag, istream &datastrm);
void save(xml_file::tag &cfgtag, ostream &datastrm);
wFont *convert(SDL_PixelFormat &fmt,Uint32 flags) const;
wFont *convertToDisplay() const;
wFont *convertForAcceleratedBlit() const;

private:
void copyDataBlock(wFont const &src);
};

#endif
-------------- next part --------------
// ----------------------------------------------------------------------------
// Description : SDL audio wrappers
// ----------------------------------------------------------------------------
// Remarks : wAudioStream::mixTo is called within the audio thread.
// Things requiring I/O and similar things had better go into
// wAudioStream::tick(). Its execution is automatically protected by
// the audio lock.
//
// ----------------------------------------------------------------------------
// © Copyright 2000 by Team WAM
// ----------------------------------------------------------------------------

#ifndef WAM_SDL_AUDIO
#define WAM_SDL_AUDIO

#include
#include
#include
#include <ixlib_array.hh>
#include <ixlib_garbage.hh>
#include <ixlib_string.hh>
#include <ixlib_ring_queue.hh>
#include “output/sdl_base.hh”

typedef TSize wAudioFrequency;
typedef Uint16 wAudioLayout;
typedef Sint32 wSampleValue;

#define SDLAUDIO_MIXBITS 28
#define SDLAUDIO_SV_MAXIMUM (1 << SDLAUDIO_MIXBITS - 1)
#define SDLAUDIO_REPEAT_INF -1u

namespace wAudio {
void mix(wSampleValue *dest,void *srcstart,void *srcend,TSize destsamples,
wAudioLayout srclayout,TSize srcchannels = 1,TSize destchannels = 1,
float lvolume = 1,float rvolume = 1,float speed = 1,
void **srcpos = NULL,TSize *destcount = NULL);
void panning2volumes(float vol,float pan,float &lvol,float &rvol);
void volumes2panning(float lvol,float rvol,float &vol,float &pan);
}

struct wAudioFormat {
wAudioFrequency Frequency;
TSize Channels;
wAudioLayout Layout;

wAudioFormat(TSize freq = 22050,TSize channels = 2,wAudioLayout layout = AUDIO_S16);
void set(TSize freq = 22050,TSize channels = 2,wAudioLayout layout = AUDIO_S16);
void getFromSpec(SDL_AudioSpec const &spec);
void fillSpec(SDL_AudioSpec &spec) const;
TSize getBytesPerSampleValue() const;
TSize getBytesPerSample() const;
};

class wAudioData {
auto_array Data;
wAudioFormat Format;
TSize Size; // samples
TSize UseCount;

public:
wAudioData();
TSize getSampleCount() const {
return Size;
}
wAudioFormat const &getFormat() const {
return Format;
}
TByte const *get() const {
return Data.get();
}

wAudioData *convertTo(wAudioFormat const &fmt);
void loadWAV(istream &istr);
TSize getByteSize() const {
  return Size * Format.getBytesPerSample();
  }
float getDuration() const {
  return Size / (float) Format.Frequency;
  }

};

class wAudioStream;
class wAudioManager {
protected:
public:
typedef unsigned wStreamId;
struct wStreamInfo {
wStreamId Id;
ref Stream;
};

struct wNotificationHandler {
  enum wNotificationType { STARTED,ENDED,REMOVED };
  virtual ~wNotificationHandler() {
    }
  virtual void operator()(wNotificationType type,wStreamId id) = 0;
  };

protected:
// mandatory-lock data
typedef vector wStreamList;
wStreamList Streams;
wStreamId NextId;

// only accessed in callback
auto_array<wSampleValue>            MixBuffer;
auto_array<TByte>			ConvertedMixBuffer;
TSize                               CallbackCalls;

// read-only
TSize                               BufferSize; // samples
SDL_AudioSpec                       InternalPlayFormat;
wAudioFormat                        PlayFormat;

typedef vector<wNotificationHandler *>	wNotificationHandlerList;
wNotificationHandlerList		NotificationHandlerList;

public:
typedef wStreamList::iterator iterator;
typedef wStreamList::const_iterator const_iterator;

wAudioManager(TSize freq = 22050,TSize channels = 2,wAudioLayout layout = AUDIO_S16,
  TSize buffer = 1024);
~wAudioManager();

iterator begin() {
  return Streams.begin();
  }
const_iterator begin() const {
  return Streams.begin();
  }
iterator end() {
  return Streams.end();
  }
const_iterator end() const {
  return Streams.end();
  }
TSize size() const {
  return Streams.size();
  }

TSize countCallbacks() const {
  return CallbackCalls;
  }

wAudioFormat const &getPlayFormat() const;
// advisory: how many samples one mixTo will maximally request
TSize getMaxRequestLength() const;

ref<wAudioStream> getStream(wStreamId id) const;
wStreamId addStream(ref<wAudioStream> strm);
void removeStream(wStreamId id);
bool isStreamActive(wStreamId id) const;
void addNotificationHandler(wNotificationHandler *handler);
void removeNotificationHandler(wNotificationHandler *handler);

void tick();

void pause();
void play();

// private:
void callback(void *stream,TSize len);

private:
void notify(wNotificationHandler::wNotificationType type,wStreamId id) const;
void cleanStreams();
wStreamList::iterator getStreamIterator(wStreamId id);
wStreamList::const_iterator getStreamIterator(wStreamId id) const;
};

class wAudioStream {
protected:
wAudioManager *Manager;
float LVolume,RVolume;

public:
wAudioStream(float vol = 1,float panning = 0);
virtual ~wAudioStream() {
}
virtual string describe() = 0;

void setVolume(float vol = 1,float panning = 0);
float getVolume() const;
float getPanning() const;

protected:
virtual void startUp(wAudioManager *mgr);
virtual void tick() = 0;
virtual void mixTo(wSampleValue *data,TSize samples,TSize frequency,TSize channels) = 0;
virtual bool isDone() const = 0;

friend class wAudioManager;
};

class wAudioTestStream : public wAudioStream {
private:
typedef wAudioStream Super;
float Angle;
float SineFrequency;

public:
wAudioTestStream(float sinefreq = 440,float vol = 1,float panning = 0);
string describe();
void tick();
void mixTo(wSampleValue *data,TSize samples,TSize frequency,TSize channels);
bool isDone() const {
return false;
}
};

class wAudioDataStream : public wAudioStream {
typedef wAudioStream Super;
wAudioData &Data;
TByte const *Position;
TSize RepeatCount;
float Delay;
TSize DelaySamples;
wAudioFormat const &Format;

public:
wAudioDataStream(wAudioData &data,TSize repeatcount = 1,float delay = 0,float vol = 1,float panning = 0);
string describe();
void startUp(wAudioManager *mgr);
void tick();
void mixTo(wSampleValue *data,TSize samples,TSize frequency,TSize channels);
bool isDone() const;

wAudioData const &getData() const {
  return Data;
  }

};

class wAudioBufferedStream : public wAudioStream {
private:
typedef wAudioStream Super;
float Speed;
ring_queue SoundBuffer;
wAudioFormat Format;
TSize BytesPerSample;

public:
wAudioBufferedStream(float vol = 1,float panning = 0);
void startUp(wAudioManager *mgr);
void tick();
void mixTo(wSampleValue *data,TSize samples,TSize frequency,TSize channels);
bool isDone() const;

protected:
virtual void internalStartUp(wAudioManager *mgr,wAudioFormat &fmt) = 0;
virtual bool isEndOfStream() const = 0;
virtual TSize getStreamData(void *data,TSize maxsize) = 0;
};

#endif