- Added support for 24-bit screenshots, so now accelerated 2D screenshots

can work.
- Tweaked the box splitting algorithm for packed textures to hopefully
  produce less wasted space.


SVN r696 (trunk)
This commit is contained in:
Randy Heit 2008-01-12 06:27:13 +00:00
parent 88549aebcd
commit 9902d73a24
10 changed files with 862 additions and 230 deletions

View file

@ -1,11 +1,15 @@
January 11, 2008
- Added support for 24-bit screenshots, so now accelerated 2D screenshots
can work.
- Tweaked the box splitting algorithm for packed textures to hopefully
produce less wasted space.
- For compatibility with the software renderer, D3DFB::DrawTextureV needs to
truncate the coordinates to integers before sending them to the hardware.
Otherwise, there can be one pixel gaps compared to the software renderer,
because the hardware is rounding to nearest but the software renderer is
simply truncating the fractional part of the coordinate. This is the real
cause of the gap at the top of the status bar at 1152x864 (and another gap
to the left of the status bar at 800x500).
cause of the gap above the status bar at 1152x864 (and another gap to the
left of the status bar at 800x500).
- Fixed: When D3DFB::DrawTextureV had to clip a tile, it adjusted the
texture coordinates erroneously, still using the old calculations from
before texture packing was implemented.

View file

@ -1977,7 +1977,7 @@ static void PutSavePic (FILE *file, int width, int height)
P_CheckPlayerSprites();
R_RenderViewToCanvas (players[consoleplayer].mo, pic, 0, 0, width, height);
screen->GetFlashedPalette (palette);
M_CreatePNG (file, pic, palette);
M_CreatePNG (file, pic->GetBuffer(), palette, SS_PAL, width, height, pic->GetPitch());
pic->Unlock ();
delete pic;
}

View file

@ -432,24 +432,21 @@ typedef struct
//
// WritePCXfile
//
void WritePCXfile (FILE *file, const DCanvas *canvas, const PalEntry *palette)
void WritePCXfile (FILE *file, const BYTE *buffer, const PalEntry *palette,
ESSType color_type, int width, int height, int pitch)
{
BYTE temprow[MAXWIDTH * 3];
const BYTE *data;
int x, y;
int runlen;
int bytes_per_row_minus_one;
BYTE color;
pcx_t pcx;
BYTE *data;
int width, height, pitch;
data = canvas->GetBuffer ();
width = canvas->GetWidth ();
height = canvas->GetHeight ();
pitch = canvas->GetPitch ();
pcx.manufacturer = 10; // PCX id
pcx.version = 5; // 256 color
pcx.version = 5; // 256 (or more) colors
pcx.encoding = 1;
pcx.bits_per_pixel = 8; // 256 color
pcx.bits_per_pixel = 8; // 256 (or more) colors
pcx.xmin = 0;
pcx.ymin = 0;
pcx.xmax = LittleShort(width-1);
@ -458,20 +455,52 @@ void WritePCXfile (FILE *file, const DCanvas *canvas, const PalEntry *palette)
pcx.vdpi = LittleShort(75);
memset (pcx.palette, 0, sizeof(pcx.palette));
pcx.reserved = 0;
pcx.color_planes = 1; // chunky image
pcx.color_planes = (color_type == SS_PAL) ? 1 : 3; // chunky image
pcx.bytes_per_line = width + (width & 1);
pcx.palette_type = 1; // not a grey scale
memset (pcx.filler, 0, sizeof(pcx.filler));
fwrite (&pcx, 128, 1, file);
bytes_per_row_minus_one = ((color_type == SS_PAL) ? width : width * 3) - 1;
// pack the image
for (y = height; y > 0; y--)
{
switch (color_type)
{
case SS_PAL:
data = buffer;
break;
case SS_RGB:
// Unpack RGB into separate planes.
for (int i = 0; i < width; ++i)
{
temprow[i ] = buffer[i*3];
temprow[i + width ] = buffer[i*3 + 1];
temprow[i + width * 2] = buffer[i*3 + 2];
}
data = temprow;
break;
case SS_BGRA:
// Unpack RGB into separate planes, discarding A.
for (int i = 0; i < width; ++i)
{
temprow[i ] = buffer[i*4 + 2];
temprow[i + width ] = buffer[i*4 + 1];
temprow[i + width * 2] = buffer[i*4];
}
data = temprow;
break;
}
buffer += pitch;
color = *data++;
runlen = 1;
for (x = width - 1; x > 0; x--)
for (x = bytes_per_row_minus_one; x > 0; x--)
{
if (*data == color)
{
@ -522,11 +551,11 @@ void WritePCXfile (FILE *file, const DCanvas *canvas, const PalEntry *palette)
if (width & 1)
putc (0, file);
data += pitch - width;
}
// write the palette
if (color_type == SS_PAL)
{
putc (12, file); // palette ID byte
for (x = 0; x < 256; x++, palette++)
{
@ -534,14 +563,16 @@ void WritePCXfile (FILE *file, const DCanvas *canvas, const PalEntry *palette)
putc (palette->g, file);
putc (palette->b, file);
}
}
}
//
// WritePNGfile
//
void WritePNGfile (FILE *file, const DCanvas *canvas, const PalEntry *palette)
void WritePNGfile (FILE *file, const BYTE *buffer, const PalEntry *palette,
ESSType color_type, int width, int height, int pitch)
{
if (!M_CreatePNG (file, canvas, palette) ||
if (!M_CreatePNG (file, buffer, palette, color_type, width, height, pitch) ||
!M_AppendPNGText (file, "Software", GAMENAME DOTVERSIONSTR) ||
!M_FinishPNG (file))
{
@ -572,8 +603,9 @@ static bool FindFreeName (FString &fullname, const char *extension)
void M_ScreenShot (const char *filename)
{
FILE *file;
FString autoname;
bool writepcx = screen->CanWritePCX() && (stricmp (screenshot_type, "pcx") == 0); // PNG is the default
bool writepcx = (stricmp (screenshot_type, "pcx") == 0); // PNG is the default
// find a file name to save it to
if (filename == NULL || filename[0] == '\0')
@ -614,46 +646,51 @@ void M_ScreenShot (const char *filename)
CreatePath(screenshot_dir);
// save the screenshot
screen->Save(autoname, writepcx);
const BYTE *buffer;
int pitch;
ESSType color_type;
screen->GetScreenshotBuffer(buffer, pitch, color_type);
if (buffer != NULL)
{
PalEntry palette[256];
if (color_type == SS_PAL)
{
screen->GetFlashedPalette(palette);
}
file = fopen (autoname, "wb");
if (file == NULL)
{
Printf ("Could not open %s\n", autoname.GetChars());
screen->ReleaseScreenshotBuffer();
return;
}
if (writepcx)
{
WritePCXfile(file, buffer, palette, color_type,
screen->GetWidth(), screen->GetHeight(), pitch);
}
else
{
WritePNGfile(file, buffer, palette, color_type,
screen->GetWidth(), screen->GetHeight(), pitch);
}
fclose(file);
screen->ReleaseScreenshotBuffer();
if (!screenshot_quiet)
{
Printf ("Captured %s\n", autoname.GetChars());
}
}
bool DCanvas::CanWritePCX()
{
return true;
}
void DCanvas::Save(const char *filename, bool writepcx)
{
FILE *file;
Lock (true);
PalEntry palette[256];
screen->GetFlashedPalette (palette);
file = fopen (filename, "wb");
if (file == NULL)
{
Printf ("Could not open %s\n", filename);
Unlock ();
return;
}
if (writepcx)
{
WritePCXfile (file, this, palette);
}
else
{
WritePNGfile (file, this, palette);
if (!screenshot_quiet)
{
Printf ("Could not create screenshot.\n");
}
}
fclose (file);
Unlock ();
}
CCMD (screenshot)

View file

@ -94,7 +94,6 @@ PNGHandle::~PNGHandle ()
static inline void MakeChunk (void *where, DWORD type, size_t len);
static inline void StuffPalette (const PalEntry *from, BYTE *to);
static bool StuffBitmap (const DCanvas *canvas, FILE *file);
static bool WriteIDAT (FILE *file, const BYTE *data, int len);
static void UnfilterRow (int width, BYTE *dest, BYTE *stream, BYTE *prev, int bpp);
static void UnpackPixels (int width, int bytesPerRow, int bitdepth, const BYTE *rowin, BYTE *rowout);
@ -125,7 +124,8 @@ CVAR(Float, png_gamma, 0.f, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
//
//==========================================================================
bool M_CreatePNG (FILE *file, const DCanvas *canvas, const PalEntry *palette)
bool M_CreatePNG (FILE *file, const BYTE *buffer, const PalEntry *palette,
ESSType color_type, int width, int height, int pitch)
{
BYTE work[8 + // signature
12+2*4+5 + // IHDR
@ -135,14 +135,15 @@ bool M_CreatePNG (FILE *file, const DCanvas *canvas, const PalEntry *palette)
IHDR *const ihdr = (IHDR *)&work[8 + 8];
DWORD *const gama = (DWORD *)((BYTE *)ihdr + 2*4+5 + 12);
BYTE *const plte = (BYTE *)gama + 4 + 12;
size_t work_len;
sig[0] = MAKE_ID(137,'P','N','G');
sig[1] = MAKE_ID(13,10,26,10);
ihdr->Width = BigLong (canvas->GetWidth ());
ihdr->Height = BigLong (canvas->GetHeight ());
ihdr->Width = BigLong(width);
ihdr->Height = BigLong(height);
ihdr->BitDepth = 8;
ihdr->ColorType = 3;
ihdr->ColorType = color_type == SS_PAL ? 3 : 2;
ihdr->Compression = 0;
ihdr->Filter = 0;
ihdr->Interlace = 0;
@ -152,13 +153,21 @@ bool M_CreatePNG (FILE *file, const DCanvas *canvas, const PalEntry *palette)
*gama = BigLong (int (45454.5f * (png_gamma == 0.f ? Gamma : png_gamma)));
MakeChunk (gama, MAKE_ID('g','A','M','A'), 4);
if (color_type == SS_PAL)
{
StuffPalette (palette, plte);
MakeChunk (plte, MAKE_ID('P','L','T','E'), 256*3);
work_len = sizeof(work);
}
else
{
work_len = sizeof(work) - (12+256*3);
}
if (fwrite (work, 1, sizeof(work), file) != sizeof(work))
if (fwrite (work, 1, work_len, file) != work_len)
return false;
return StuffBitmap (canvas, file);
return M_SaveBitmap (buffer, color_type, width, height, pitch, file);
}
//==========================================================================
@ -707,7 +716,7 @@ static inline void MakeChunk (void *where, DWORD type, size_t len)
//
//==========================================================================
static inline void StuffPalette (const PalEntry *from, BYTE *to)
static void StuffPalette (const PalEntry *from, BYTE *to)
{
for (int i = 256; i > 0; --i)
{
@ -721,27 +730,173 @@ static inline void StuffPalette (const PalEntry *from, BYTE *to)
//==========================================================================
//
// StuffBitmap
// CalcSum
//
//
//==========================================================================
DWORD CalcSum(Byte *row, int len)
{
DWORD sum = 0;
while (len-- != 0)
{
sum += (char)*row++;
}
return sum;
}
//==========================================================================
//
// SelectFilter
//
// Performs the heuristic recommended by the PNG spec to decide the
// (hopefully) best filter to use for this row. To quate:
//
// Select the filter that gives the smallest sum of absolute values of
// outputs. (Consider the output bytes as signed differences for this
// test.)
//
//==========================================================================
static int SelectFilter(Byte row[5][1 + MAXWIDTH*3], Byte prior[MAXWIDTH], int width)
{
#if 1
// As it turns out, it seems no filtering is the best for Doom screenshots,
// no matter what the heuristic might determine.
return 0;
#else
DWORD sum;
DWORD bestsum;
int bestfilter;
int x;
width *= 3;
// The first byte of each row holds the filter type, filled in by the caller.
// However, the prior row does not contain a filter type, since it's always 0.
bestsum = 0;
bestfilter = 0;
// None
for (x = 1; x <= width; ++x)
{
bestsum += abs((char)row[0][x]);
}
// Sub
row[1][1] = row[0][1];
row[1][2] = row[0][2];
row[1][3] = row[0][3];
sum = abs((char)row[0][1]) + abs((char)row[0][2]) + abs((char)row[0][3]);
for (x = 4; x <= width; ++x)
{
row[1][x] = row[0][x] - row[0][x - 3];
sum += abs((char)row[1][x]);
if (sum >= bestsum)
{ // This isn't going to be any better.
break;
}
}
if (sum < bestsum)
{
bestsum = sum;
bestfilter = 1;
}
// Up
sum = 0;
for (x = 1; x <= width; ++x)
{
row[2][x] = row[0][x] - prior[x - 1];
sum += abs((char)row[2][x]);
if (sum >= bestsum)
{ // This isn't going to be any better.
break;
}
}
if (sum < bestsum)
{
bestsum = sum;
bestfilter = 2;
}
// Average
row[3][1] = row[0][1] - prior[0] / 2;
row[3][2] = row[0][2] - prior[1] / 2;
row[3][3] = row[0][3] - prior[2] / 2;
sum = abs((char)row[3][1]) + abs((char)row[3][2]) + abs((char)row[3][3]);
for (x = 4; x <= width; ++x)
{
row[3][x] = row[0][x] - (row[0][x - 3] + prior[x - 1]) / 2;
sum += (char)row[3][x];
if (sum >= bestsum)
{ // This isn't going to be any better.
break;
}
}
if (sum < bestsum)
{
bestsum = sum;
bestfilter = 3;
}
// Paeth
row[4][1] = row[0][1] - prior[0];
row[4][2] = row[0][2] - prior[1];
row[4][3] = row[0][3] - prior[2];
sum = abs((char)row[4][1]) + abs((char)row[4][2]) + abs((char)row[4][3]);
for (x = 4; x <= width; ++x)
{
Byte a = row[0][x - 3];
Byte b = prior[x - 1];
Byte c = prior[x - 4];
int p = a + b - c;
int pa = abs(p - a);
int pb = abs(p - b);
int pc = abs(p - c);
if (pa <= pb && pa <= pc)
{
row[4][x] = row[0][x] - a;
}
else if (pb <= pc)
{
row[4][x] = row[0][x] - b;
}
else
{
row[4][x] = row[0][x] - c;
}
sum += (char)row[4][x];
if (sum >= bestsum)
{ // This isn't going to be any better.
break;
}
}
if (sum < bestsum)
{
bestfilter = 4;
}
return bestfilter;
#endif
}
//==========================================================================
//
// M_SaveBitmap
//
// Given a bitmap, creates one or more IDAT chunks in the given file.
// Returns true on success.
//
//==========================================================================
static bool StuffBitmap (const DCanvas *canvas, FILE *file)
{
const int pitch = canvas->GetPitch();
const int width = canvas->GetWidth();
const int height = canvas->GetHeight();
BYTE *from = canvas->GetBuffer();
return M_SaveBitmap(from, width, height, pitch, file);
}
bool M_SaveBitmap(BYTE * from, int width, int height, int pitch, FILE *file)
bool M_SaveBitmap(const BYTE *from, ESSType color_type, int width, int height, int pitch, FILE *file)
{
Byte prior[MAXWIDTH];
Byte buffer[PNG_WRITE_SIZE];
Byte zero = 0;
Byte temprow[5][1 + MAXWIDTH*3];
z_stream stream;
int err;
int y;
@ -761,24 +916,56 @@ bool M_SaveBitmap(BYTE * from, int width, int height, int pitch, FILE *file)
stream.next_out = buffer;
stream.avail_out = sizeof(buffer);
while (y > 0 && err == Z_OK)
temprow[0][0] = 0;
temprow[1][0] = 1;
temprow[2][0] = 2;
temprow[3][0] = 3;
temprow[4][0] = 4;
// Fill the prior row to 0 for RGB images. Paletted is always filter 0,
// so it doesn't need this.
if (color_type != SS_PAL)
{
y--;
for (int i = 2; i && err == Z_OK; --i)
{
const int flushiness = (y == 0 && i == 1) ? Z_FINISH : 0;
if (i == 2)
{ // always use filter type 0
stream.next_in = &zero;
stream.avail_in = 1;
memset(prior, 0, width * 3);
}
else
while (y-- > 0 && err == Z_OK)
{
stream.next_in = from;
stream.avail_in = width;
switch (color_type)
{
case SS_PAL:
memcpy(&temprow[0][1], from, width);
// always use filter type 0 for paletted images
stream.next_in = temprow[0];
stream.avail_in = width + 1;
break;
case SS_RGB:
memcpy(&temprow[0][1], from, width*3);
stream.next_in = temprow[SelectFilter(temprow, prior, width)];
stream.avail_in = width * 3 + 1;
break;
case SS_BGRA:
for (int x = 0; x < width; ++x)
{
temprow[0][x*3 + 1] = from[x*4 + 2];
temprow[0][x*3 + 2] = from[x*4 + 1];
temprow[0][x*3 + 3] = from[x*4];
}
stream.next_in = temprow[SelectFilter(temprow, prior, width)];
stream.avail_in = width * 3 + 1;
break;
}
if (color_type != SS_PAL)
{
// Save this row for filter calculations on the next row.
memcpy (prior, &temprow[0][1], stream.avail_in - 1);
}
from += pitch;
}
err = deflate (&stream, flushiness);
err = deflate (&stream, (y == 0) ? Z_FINISH : 0);
if (err != Z_OK)
{
break;
@ -793,7 +980,7 @@ bool M_SaveBitmap(BYTE * from, int width, int height, int pitch, FILE *file)
stream.avail_out = sizeof(buffer);
if (stream.avail_in != 0)
{
err = deflate (&stream, flushiness);
err = deflate (&stream, (y == 0) ? Z_FINISH : 0);
if (err != Z_OK)
{
break;
@ -801,7 +988,6 @@ bool M_SaveBitmap(BYTE * from, int width, int height, int pitch, FILE *file)
}
}
}
}
while (err == Z_OK)
{

View file

@ -39,7 +39,8 @@
// The passed file should be a newly created file.
// This function writes the PNG signature and the IHDR, gAMA, PLTE, and IDAT
// chunks.
bool M_CreatePNG (FILE *file, const DCanvas *canvas, const PalEntry *pal);
bool M_CreatePNG (FILE *file, const BYTE *buffer, const PalEntry *pal,
ESSType color_type, int width, int height, int pitch);
// Creates a grayscale 1x1 PNG file. Used for savegames without savepics.
bool M_CreateDummyPNG (FILE *file);
@ -53,7 +54,7 @@ bool M_AppendPNGText (FILE *file, const char *keyword, const char *text);
// Appends the IEND chunk to a PNG file.
bool M_FinishPNG (FILE *file);
bool M_SaveBitmap(BYTE * from, int width, int height, int pitch, FILE *file);
bool M_SaveBitmap(const BYTE *from, ESSType color_type, int width, int height, int pitch, FILE *file);
// PNG Reading --------------------------------------------------------------

View file

@ -184,6 +184,12 @@ void V_MarkRect (int x, int y, int width, int height)
DCanvas *DCanvas::CanvasChain = NULL;
//==========================================================================
//
// DCanvas Constructor
//
//==========================================================================
DCanvas::DCanvas (int _width, int _height)
{
// Init member vars
@ -198,6 +204,12 @@ DCanvas::DCanvas (int _width, int _height)
CanvasChain = this;
}
//==========================================================================
//
// DCanvas Destructor
//
//==========================================================================
DCanvas::~DCanvas ()
{
// Remove from list of active canvases
@ -218,6 +230,12 @@ DCanvas::~DCanvas ()
}
}
//==========================================================================
//
// DCanvas :: IsValid
//
//==========================================================================
bool DCanvas::IsValid ()
{
// A nun-subclassed DCanvas is never valid
@ -255,8 +273,14 @@ void DCanvas::FlatFill (int left, int top, int right, int bottom, FTexture *src,
}
}
//==========================================================================
//
// DCanvas :: Clear
//
// Set an area to a specified color.
//
//==========================================================================
// [RH] Set an area to a specified color
void DCanvas::Clear (int left, int top, int right, int bottom, int palcolor, uint32 color)
{
int x, y;
@ -298,6 +322,15 @@ void DCanvas::Clear (int left, int top, int right, int bottom, int palcolor, uin
}
}
//==========================================================================
//
// DCanvas :: Dim
//
// Applies a colored overlay to the entire screen, with the opacity
// determined by the dimamount cvar.
//
//==========================================================================
void DCanvas::Dim (PalEntry color)
{
PalEntry dimmer;
@ -320,6 +353,14 @@ void DCanvas::Dim (PalEntry color)
Dim (dimmer, amount, 0, 0, Width, Height);
}
//==========================================================================
//
// DCanvas :: Dim
//
// Applies a colored overlay to an area of the screen.
//
//==========================================================================
void DCanvas::Dim (PalEntry color, float damount, int x1, int y1, int w, int h)
{
if (damount == 0.f)
@ -359,11 +400,59 @@ void DCanvas::Dim (PalEntry color, float damount, int x1, int y1, int w, int h)
}
}
//==========================================================================
//
// DCanvas :: UsesColormap
//
//==========================================================================
bool DCanvas::UsesColormap() const
{
return true;
}
//==========================================================================
//
// DCanvas :: GetScreenshotBuffer
//
// Returns a buffer containing the most recently displayed frame. The
// width and height of this buffer are the same as the canvas.
//
//==========================================================================
void DCanvas::GetScreenshotBuffer(const BYTE *&buffer, int &pitch, ESSType &color_type)
{
Lock(true);
buffer = GetBuffer();
pitch = GetPitch();
color_type = SS_PAL;
}
//==========================================================================
//
// DCanvas :: ReleaseScreenshotBuffer
//
// Releases the buffer obtained through GetScreenshotBuffer. These calls
// must not be nested.
//
//==========================================================================
void DCanvas::ReleaseScreenshotBuffer()
{
Unlock();
}
//==========================================================================
//
// V_GetColorFromString
//
// Passed a string of the form "#RGB", "#RRGGBB", "R G B", or "RR GG BB",
// returns a number representing that color. If palette is non-NULL, the
// index of the best match in the palette is returned, otherwise the
// RRGGBB value is returned directly.
//
//==========================================================================
int V_GetColorFromString (const DWORD *palette, const char *cstr)
{
int c[3], i, p;
@ -434,11 +523,20 @@ int V_GetColorFromString (const DWORD *palette, const char *cstr)
}
}
if (palette)
return ColorMatcher.Pick (c[0]>>8, c[1]>>8, c[2]>>8);
return ColorMatcher.Pick (c[0], c[1], c[2]);
else
return MAKERGB(c[0], c[1], c[2]);
}
//==========================================================================
//
// V_GetColorStringByName
//
// Searches for the given color name in x11r6rgb.txt and returns an
// HTML-ish "#RRGGBB" string for it if found or the empty string if not.
//
//==========================================================================
FString V_GetColorStringByName (const char *name)
{
FMemLump rgbNames;
@ -520,6 +618,14 @@ FString V_GetColorStringByName (const char *name)
return FString();
}
//==========================================================================
//
// V_GetColor
//
// Works like V_GetColorFromString(), but also understands X11 color names.
//
//==========================================================================
int V_GetColor (const DWORD *palette, const char *str)
{
FString string = V_GetColorStringByName (str);
@ -536,7 +642,14 @@ int V_GetColor (const DWORD *palette, const char *str)
return res;
}
//==========================================================================
//
// BuildTransTable
//
// Build the tables necessary for blending
//
//==========================================================================
static void BuildTransTable (const PalEntry *palette)
{
int r, g, b;
@ -570,6 +683,12 @@ static void BuildTransTable (const PalEntry *palette)
Col2RGB8_LessPrecision[64] = Col2RGB8[64];
}
//==========================================================================
//
// DCanvas :: CalcGamma
//
//==========================================================================
void DCanvas::CalcGamma (float gamma, BYTE gammalookup[256])
{
// I found this formula on the web at
@ -585,6 +704,14 @@ void DCanvas::CalcGamma (float gamma, BYTE gammalookup[256])
}
}
//==========================================================================
//
// DSimpleCanvas Constructor
//
// A simple canvas just holds a buffer in main memory.
//
//==========================================================================
DSimpleCanvas::DSimpleCanvas (int width, int height)
: DCanvas (width, height)
{
@ -627,6 +754,12 @@ DSimpleCanvas::DSimpleCanvas (int width, int height)
memset (MemBuffer, 0, Pitch * height);
}
//==========================================================================
//
// DSimpleCanvas Destructor
//
//==========================================================================
DSimpleCanvas::~DSimpleCanvas ()
{
if (MemBuffer != NULL)
@ -636,11 +769,23 @@ DSimpleCanvas::~DSimpleCanvas ()
}
}
//==========================================================================
//
// DSimpleCanvas :: IsValid
//
//==========================================================================
bool DSimpleCanvas::IsValid ()
{
return (MemBuffer != NULL);
}
//==========================================================================
//
// DSimpleCanvas :: Lock
//
//==========================================================================
bool DSimpleCanvas::Lock ()
{
if (LockCount == 0)
@ -651,6 +796,12 @@ bool DSimpleCanvas::Lock ()
return false; // System surfaces are never lost
}
//==========================================================================
//
// DSimpleCanvas :: Unlock
//
//==========================================================================
void DSimpleCanvas::Unlock ()
{
if (--LockCount <= 0)
@ -660,6 +811,15 @@ void DSimpleCanvas::Unlock ()
}
}
//==========================================================================
//
// DFrameBuffer Constructor
//
// A frame buffer canvas is the most common and represents the image that
// gets drawn to the screen.
//
//==========================================================================
DFrameBuffer::DFrameBuffer (int width, int height)
: DSimpleCanvas (width, height)
{
@ -667,6 +827,14 @@ DFrameBuffer::DFrameBuffer (int width, int height)
Accel2D = false;
}
//==========================================================================
//
// DFrameBuffer :: DrawRateStuff
//
// Draws the fps counter, dot ticker, and palette debug.
//
//==========================================================================
void DFrameBuffer::DrawRateStuff ()
{
// Draws frame time and cumulative fps
@ -742,6 +910,14 @@ void DFrameBuffer::DrawRateStuff ()
}
}
//==========================================================================
//
// FPaleteTester Constructor
//
// This is just a 16x16 image with every possible color value.
//
//==========================================================================
FPaletteTester::FPaletteTester()
{
Width = 16;
@ -754,11 +930,23 @@ FPaletteTester::FPaletteTester()
MakeTexture();
}
//==========================================================================
//
// FPaletteTester :: CheckModified
//
//==========================================================================
bool FPaletteTester::CheckModified()
{
return CurTranslation != WantTranslation;
}
//==========================================================================
//
// FPaletteTester :: SetTranslation
//
//==========================================================================
void FPaletteTester::SetTranslation(int num)
{
if (num >= 1 && num <= 9)
@ -767,10 +955,22 @@ void FPaletteTester::SetTranslation(int num)
}
}
//==========================================================================
//
// FPaletteTester :: Unload
//
//==========================================================================
void FPaletteTester::Unload()
{
}
//==========================================================================
//
// FPaletteTester :: GetColumn
//
//==========================================================================
const BYTE *FPaletteTester::GetColumn (unsigned int column, const Span **spans_out)
{
if (CurTranslation != WantTranslation)
@ -785,6 +985,12 @@ const BYTE *FPaletteTester::GetColumn (unsigned int column, const Span **spans_o
return Pixels + column*16;
}
//==========================================================================
//
// FPaletteTester :: GetPixels
//
//==========================================================================
const BYTE *FPaletteTester::GetPixels ()
{
if (CurTranslation != WantTranslation)
@ -794,6 +1000,12 @@ const BYTE *FPaletteTester::GetPixels ()
return Pixels;
}
//==========================================================================
//
// FPaletteTester :: MakeTexture
//
//==========================================================================
void FPaletteTester::MakeTexture()
{
int i, j, k, t;
@ -814,6 +1026,15 @@ void FPaletteTester::MakeTexture()
CurTranslation = t;
}
//==========================================================================
//
// DFrameBuffer :: CopyFromBuff
//
// Copies pixels from main memory to video memory. This is only used by
// DDrawFB.
//
//==========================================================================
void DFrameBuffer::CopyFromBuff (BYTE *src, int srcPitch, int width, int height, BYTE *dest)
{
if (Pitch == width && Pitch == Width && srcPitch == width)
@ -831,46 +1052,122 @@ void DFrameBuffer::CopyFromBuff (BYTE *src, int srcPitch, int width, int height,
}
}
//==========================================================================
//
// DFrameBuffer :: SetVSync
//
// Turns vertical sync on and off, if supported.
//
//==========================================================================
void DFrameBuffer::SetVSync (bool vsync)
{
}
//==========================================================================
//
// DFrameBuffer :: SetBlendingRect
//
// Defines the area of the screen containing the 3D view.
//
//==========================================================================
void DFrameBuffer::SetBlendingRect (int x1, int y1, int x2, int y2)
{
}
//==========================================================================
//
// DFrameBuffer :: Begin2D
//
// Signal that 3D rendering is complete, and the rest of the operations on
// the canvas until Unlock() will be 2D ones.
//
//==========================================================================
bool DFrameBuffer::Begin2D (bool copy3d)
{
return false;
}
//==========================================================================
//
// DFrameBuffer :: CreateTexture
//
// Creates a native texture for a game texture, if supported.
//
//==========================================================================
FNativeTexture *DFrameBuffer::CreateTexture(FTexture *gametex, bool wrapping)
{
return NULL;
}
//==========================================================================
//
// DFrameBuffer :: CreatePalette
//
// Creates a native palette from a remap table, if supported.
//
//==========================================================================
FNativePalette *DFrameBuffer::CreatePalette(FRemapTable *remap)
{
return NULL;
}
//==========================================================================
//
// DFrameBuffer :: WipeStartScreen
//
// Grabs a copy of the screen currently displayed to serve as the initial
// frame of a screen wipe. Also determines which screenwipe will be
// performed.
//
//==========================================================================
bool DFrameBuffer::WipeStartScreen(int type)
{
return wipe_StartScreen(type);
}
//==========================================================================
//
// DFrameBuffer :: WipeEndScreen
//
// Grabs a copy of the most-recently drawn, but not yet displayed, screen
// to serve as the final frame of a screen wipe.
//
//==========================================================================
void DFrameBuffer::WipeEndScreen()
{
wipe_EndScreen();
Unlock();
}
//==========================================================================
//
// DFrameBuffer :: WipeDo
//
// Draws one frame of a screenwipe. Should be called no more than 35
// times per second. If called less than that, ticks indicates how many
// ticks have passed since the last call.
//
//==========================================================================
bool DFrameBuffer::WipeDo(int ticks)
{
Lock(true);
return wipe_ScreenWipe(ticks);
}
//==========================================================================
//
// DFrameBuffer :: WipeCleanup
//
//==========================================================================
void DFrameBuffer::WipeCleanup()
{
wipe_Cleanup();

View file

@ -117,13 +117,18 @@ enum
HUD_HorizCenter
};
// Screenshot buffer image data types
enum ESSType
{
SS_PAL,
SS_RGB,
SS_BGRA
};
//
// VIDEO
//
// [RH] Made screens more implementation-independant:
// This layer isn't really necessary, and it would be nice to remove it, I think.
// But ZDoom is now built around it so much, I'll probably just leave it.
//
class DCanvas : public DObject
{
@ -178,11 +183,13 @@ public:
// Can be overridden so that the colormaps for sector color/fade won't be built.
virtual bool UsesColormap() const;
// software renderer always returns true but other renderers may not want to implement PCX.
bool CanWritePCX();
// Retrieves a buffer containing image data for a screenshot.
// Hint: Pitch can be negative for upside-down images, in which case buffer
// points to the last row in the buffer, which will be the first row output.
virtual void GetScreenshotBuffer(const BYTE *&buffer, int &pitch, ESSType &color_type);
// Saves canvas to a file
void Save(const char *filename, bool writepcx);
// Releases the screenshot buffer.
virtual void ReleaseScreenshotBuffer();
// Text drawing functions -----------------------------------------------

View file

@ -227,6 +227,8 @@ D3DFB::D3DFB (int width, int height, bool fullscreen)
FBTexture = NULL;
TempRenderTexture = NULL;
InitialWipeScreen = NULL;
ScreenshotTexture = NULL;
ScreenshotSurface = NULL;
FinalWipeScreen = NULL;
PaletteTexture = NULL;
StencilPaletteTexture = NULL;
@ -439,6 +441,8 @@ void D3DFB::ReleaseResources ()
KillNativeTexs();
KillNativePals();
ReleaseDefaultPoolItems();
SAFE_RELEASE( ScreenshotSurface );
SAFE_RELEASE( ScreenshotTexture );
SAFE_RELEASE( PaletteTexture );
SAFE_RELEASE( StencilPaletteTexture );
SAFE_RELEASE( ShadedPaletteTexture );
@ -1087,6 +1091,161 @@ void D3DFB::SetBlendingRect(int x1, int y1, int x2, int y2)
BlendingRect.bottom = y2;
}
//==========================================================================
//
// D3DFB :: GetScreenshotBuffer
//
// Returns a pointer into a surface holding the current screen data.
//
//==========================================================================
void D3DFB::GetScreenshotBuffer(const BYTE *&buffer, int &pitch, ESSType &color_type)
{
D3DLOCKED_RECT lrect;
if (!test2d)
{
Super::GetScreenshotBuffer(buffer, pitch, color_type);
return;
}
buffer = NULL;
if ((ScreenshotTexture = GetCurrentScreen()) != NULL)
{
if (FAILED(ScreenshotTexture->GetSurfaceLevel(0, &ScreenshotSurface)))
{
ScreenshotTexture->Release();
ScreenshotTexture = NULL;
}
else if (FAILED(ScreenshotSurface->LockRect(&lrect, NULL, D3DLOCK_READONLY | D3DLOCK_NOSYSLOCK)))
{
ScreenshotSurface->Release();
ScreenshotSurface = NULL;
ScreenshotTexture->Release();
ScreenshotTexture = NULL;
}
else
{
buffer = (const BYTE *)lrect.pBits + lrect.Pitch * LBOffsetI;
pitch = lrect.Pitch;
color_type = SS_BGRA;
}
}
}
//==========================================================================
//
// D3DFB :: ReleaseScreenshotBuffer
//
//==========================================================================
void D3DFB::ReleaseScreenshotBuffer()
{
if (LockCount > 0)
{
Super::ReleaseScreenshotBuffer();
}
if (ScreenshotSurface != NULL)
{
ScreenshotSurface->UnlockRect();
ScreenshotSurface->Release();
ScreenshotSurface = NULL;
}
SAFE_RELEASE( ScreenshotTexture );
}
//==========================================================================
//
// D3DFB :: GetCurrentScreen
//
// Returns a texture containing the pixels currently visible on-screen.
//
//==========================================================================
IDirect3DTexture9 *D3DFB::GetCurrentScreen()
{
IDirect3DTexture9 *tex;
IDirect3DSurface9 *tsurf, *surf;
D3DSURFACE_DESC desc;
if (Windowed)
{
// The texture we read into must have the same pixel format as
// the TempRenderTexture.
if (SUCCEEDED(TempRenderTexture->GetSurfaceLevel(0, &tsurf)))
{
if (FAILED(tsurf->GetDesc(&desc)))
{
tsurf->Release();
return NULL;
}
tsurf->Release();
}
else
{
return NULL;
}
}
else
{
if (SUCCEEDED(D3DDevice->GetBackBuffer(0, 0, D3DBACKBUFFER_TYPE_MONO, &tsurf)))
{
if (FAILED(tsurf->GetDesc(&desc)))
{
tsurf->Release();
return NULL;
}
tsurf->Release();
}
else
{
return NULL;
}
// GetFrontBufferData works only with this format
desc.Format = D3DFMT_A8R8G8B8;
}
if (FAILED(D3DDevice->CreateTexture(desc.Width, desc.Height, 1, 0,
desc.Format, D3DPOOL_SYSTEMMEM, &tex, NULL)))
{
return NULL;
}
if (FAILED(tex->GetSurfaceLevel(0, &surf)))
{
tex->Release();
return NULL;
}
if (!Windowed)
{
if (FAILED(D3DDevice->GetFrontBufferData(0, surf)))
{
surf->Release();
tex->Release();
return NULL;
}
}
else
{
if (SUCCEEDED(TempRenderTexture->GetSurfaceLevel(0, &tsurf)))
{
if (FAILED(D3DDevice->GetRenderTargetData(tsurf, surf)))
{
tsurf->Release();
tex->Release();
return NULL;
}
tsurf->Release();
}
else
{
tex->Release();
return NULL;
}
}
surf->Release();
return tex;
}
/**************************************************************************/
/* 2D Stuff */
/**************************************************************************/
@ -1435,7 +1594,9 @@ void D3DFB::PackingTexture::AllocateImage(D3DFB::PackedTexture *box, int w, int
box->Prev = &UsedList;
// If we didn't use the whole box, split the remainder into the empty list.
#if 1
if (box->Area.bottom + 7 < start.bottom && box->Area.right + 7 < start.right)
{
// Split like this:
// +---+------+
// |###| |
@ -1443,7 +1604,6 @@ void D3DFB::PackingTexture::AllocateImage(D3DFB::PackedTexture *box, int w, int
// | |
// | |
// +----------+
// Empirical evidence indicates that this gives the best utilization.
if (box->Area.bottom < start.bottom)
{
AddEmptyBox(start.left, box->Area.bottom, start.right, start.bottom);
@ -1452,7 +1612,9 @@ void D3DFB::PackingTexture::AllocateImage(D3DFB::PackedTexture *box, int w, int
{
AddEmptyBox(box->Area.right, start.top, start.right, box->Area.bottom);
}
#else
}
else
{
// Split like this:
// +---+------+
// |###| |
@ -1468,7 +1630,7 @@ void D3DFB::PackingTexture::AllocateImage(D3DFB::PackedTexture *box, int w, int
{
AddEmptyBox(box->Area.right, start.top, start.right, start.bottom);
}
#endif
}
}
//==========================================================================

View file

@ -135,7 +135,7 @@ EXTERN_CVAR(Bool, test2d)
bool D3DFB::WipeStartScreen(int type)
{
IDirect3DSurface9 *surf, *tsurf;
IDirect3DSurface9 *tsurf;
D3DSURFACE_DESC desc;
if (!test2d)
@ -161,88 +161,20 @@ bool D3DFB::WipeStartScreen(int type)
return false;
}
if (Windowed)
{
// The InitialWipeScreen must have the same pixel format as
// the TempRenderTexture.
if (SUCCEEDED(TempRenderTexture->GetSurfaceLevel(0, &tsurf)))
{
if (FAILED(tsurf->GetDesc(&desc)))
{
tsurf->Release();
return false;
}
tsurf->Release();
}
else
{
return false;
}
}
else
{
if (SUCCEEDED(D3DDevice->GetBackBuffer(0, 0, D3DBACKBUFFER_TYPE_MONO, &tsurf)))
{
if (FAILED(tsurf->GetDesc(&desc)))
{
tsurf->Release();
return false;
}
tsurf->Release();
}
else
{
return false;
}
// GetFrontBufferData works only with this format
desc.Format = D3DFMT_A8R8G8B8;
}
InitialWipeScreen = GetCurrentScreen();
if (FAILED(D3DDevice->CreateTexture(desc.Width, desc.Height, 1, 0,
desc.Format, D3DPOOL_SYSTEMMEM, &InitialWipeScreen, NULL)))
{
InitialWipeScreen = NULL;
return false;
}
if (FAILED(InitialWipeScreen->GetSurfaceLevel(0, &surf)))
{
InitialWipeScreen->Release();
InitialWipeScreen = NULL;
return false;
}
if (!Windowed)
{
if (FAILED(D3DDevice->GetFrontBufferData(0, surf)))
{
surf->Release();
InitialWipeScreen->Release();
InitialWipeScreen = NULL;
return false;
}
FinalWipeScreen = TempRenderTexture;
}
else
{
if (SUCCEEDED(TempRenderTexture->GetSurfaceLevel(0, &tsurf)))
{
if (FAILED(D3DDevice->GetRenderTargetData(tsurf, surf)))
{
tsurf->Release();
InitialWipeScreen->Release();
InitialWipeScreen = NULL;
return false;
}
}
else
{
InitialWipeScreen->Release();
InitialWipeScreen = NULL;
return false;
}
// Create another texture to copy the final wipe screen to so
// we can still gamma correct the wipe. Since this is just for
// gamma correction, it's okay to fail (though not desirable.)
if (GammaFixerShader != NULL && Gamma != 1)
{
if (SUCCEEDED(TempRenderTexture->GetSurfaceLevel(0, &tsurf)))
{
if (FAILED(tsurf->GetDesc(&desc)) ||
FAILED(D3DDevice->CreateTexture(desc.Width, desc.Height,
@ -251,16 +183,17 @@ bool D3DFB::WipeStartScreen(int type)
{
FinalWipeScreen = TempRenderTexture;
}
tsurf->Release();
}
}
else
{
FinalWipeScreen = TempRenderTexture;
}
tsurf->Release();
}
surf->Release();
// Even fullscreen will render to the TempRenderTexture, so we can have
// a copy of the new screen readily available.
// Make even fullscreen model render to the TempRenderTexture, so
// we can have a copy of the new screen readily available.
GatheringWipeScreen = true;
return true;
}

View file

@ -237,6 +237,8 @@ public:
void Blank ();
bool PaintToWindow ();
void SetVSync (bool vsync);
void GetScreenshotBuffer(const BYTE *&buffer, int &pitch, ESSType &color_type);
void ReleaseScreenshotBuffer();
void SetBlendingRect (int x1, int y1, int x2, int y2);
bool Begin2D (bool copy3d);
FNativeTexture *CreateTexture (FTexture *gametex, bool wrapping);
@ -297,6 +299,7 @@ private:
void FillPresentParameters (D3DPRESENT_PARAMETERS *pp, bool fullscreen, bool vsync);
void CalcFullscreenCoords (FBVERTEX verts[4], bool viewarea_only, D3DCOLOR color0, D3DCOLOR color1) const;
bool Reset();
IDirect3DTexture9 *GetCurrentScreen();
void ReleaseDefaultPoolItems();
void KillNativePals();
void KillNativeTexs();
@ -360,6 +363,8 @@ private:
IDirect3DTexture9 *PaletteTexture;
IDirect3DTexture9 *StencilPaletteTexture;
IDirect3DTexture9 *ShadedPaletteTexture;
IDirect3DTexture9 *ScreenshotTexture;
IDirect3DSurface9 *ScreenshotSurface;
IDirect3DVertexBuffer9 *VertexBuffer;
FBVERTEX *VertexData;