protected:
// Locking and unlocking the whole surface
void Lock (SurfaceDesc & desc) {
assert (_i != 0);
HRESULT res;
do res = _i->Lock (0, &desc, 0, 0); while (res == DDERR_WASSTILLDRAWING);
if (res != DD_OK) throw "Cannot lock surface";
}
void Unlock () {
assert (_i != 0);
_i->Unlock (0);
}
public:
Surface () {}
void GetDescription (SurfaceDesc & desc) {
HRESULT res = _i->GetSurfaceDesc (&desc);
if (res != DD_OK) throw "Cannot get surface description";
}
void SetClipper (Clipper & clipper) {
assert (_i != 0);
HRESULT res = _i->SetClipper (clipper);
if (res != DD_OK) throw "Cannot set clipper";
}
void BltFrom (Surface & src, RECT * dstRect = 0, RECT * srcRect = 0) {
assert (_i != 0);
HRESULT res = _i->Blt (dstRect, src._i, srcRect, 0, 0);
if (res != DD_OK) throw "Cannot perform a blt";
}
void Fill (COLORREF color);
};
When you create a surface, it has to be one of the derived ones. The primary surface lets you draw directly on your screen, the off-screen surface lets you prepare a picture off-screen in a format that can be very quickly transferred to the screen.
class PrimarySurface: public Surface {
public:
PrimarySurface () {}
PrimarySurface (Draw & draw) {
Init (draw);
}
void Init (Draw & draw) {
SurfaceDesc desc;
desc.SetCapabilities (DDSCAPS_PRIMARYSURFACE);
HRESULT res = draw->CreateSurface (&desc, &_i, 0);
if (res != DD_OK) throw "Cannot create primary surface";
}
};
class OffScreenSurface: public Surface {
public:
OffScreenSurface () {}
OffScreenSurface (Draw & draw, int width, int height) {
Init (draw, width, height);
}
void Init (Draw & draw, int width, int height) {
SurfaceDesc desc;
desc.SetCapabilities (DDSCAPS_OFFSCREENPLAIN);
desc.SetDimensions (width, height);
HRESULT res = draw->CreateSurface (&desc, &_i, 0);
if (res != DD_OK) throw "Cannot create off-screen surface";
}
};
In order to directly access pixels in a surface, you have to lock it. The class, SurfaceBuf, encapsulates locking and unlocking in its constructor and destructor, so that you don't have to worry about it. It also provides direct access to the buffer through its SetPixel method. Notice the low-level address calculations and color formatting.
class SurfaceBuf {
public:
SurfaceBuf (Surface & surface) : _surface (surface) {
SurfaceDesc desc;
surface.Lock (desc);
_pitch = desc.Pitch ();
_buf = static_cast (desc.Buffer ());
_format.Init (desc);
int bpp = _format.BitsPp ();
if (bpp != 16 && bpp != 24 && bpp != 32) {
surface.Unlock ();
throw "Only high color and true color supported";
}
}
~SurfaceBuf () {
_surface.Unlock ();
}
void SetPixel (int x, int y, COLORREF color) {
switch (_format.BitsPp ()) {
case 16:
{
int offset = y * _pitch + x * 2;
unsigned short * p = reinterpret_cast ( _buf + offset);
*p &= _format.Mask ();
*p |= static_cast ( _format.ColorValue16 (color));
}
break;
case 24:
{
int offset = y * _pitch + x * 3;
unsigned long * p = reinterpret_cast ( _buf + offset);
*p &= _format.Mask ();
*p |= _format.ColorValue24 (color);
}
break;
case 32:
{
int offset = y * _pitch + x * 4;
unsigned long * p = reinterpret_cast ( _buf + offset);
*p &= _format.Mask ();
*p |= _format.ColorValue32 (color);
}
break;
}
}
private:
Surface & _surface;
unsigned char * _buf;
int _pitch;
PixelFormat _format;
};
There is a separate class to deal with the formatting of color values. As you can see, depending on the bits-per-pixel setting of the video card, different color encodings are used. The surface descriptor contains bit masks that are used in this encoding. These masks vary not only between bpp settings, but also between video cards. The most complex is the encoding of the color for the 6-bit setting. In 32-bit mode the card can actually support more colors that can be packed into the standard Windows COLORREF. Here, we're not making use of it, but it would be an interesting area to experiment.
class PixelFormat {
public:
PixelFormat () {}
PixelFormat (SurfaceDesc & desc) {
Init (desc);
}
void Init (SurfaceDesc & desc);
int BitsPp () const { return _bpp; }
unsigned long Mask () const { return _mask; }
unsigned long ColorValue16 (COLORREF color) {
return ( (GetRValue (color) << _redShift) & (_redMask << 16) | (GetGValue (color) << _greenShift) & (_greenMask << 16) | (GetBValue (color) << _blueShift) & (_blueMask << 16)) >> 16;
}
unsigned long ColorValue24 (COLORREF color) {
return (GetRValue (color) << 16) & _redMask | (GetGValue (color) << 8) & _greenMask | GetBValue (color) & _blueMask;
}
unsigned long ColorValue32 (COLORREF color) {
return (GetRValue (color) << 16) & _redMask | (GetGValue (color) << 8) & _greenMask | GetBValue (color) & _blueMask;
}
Читать дальше