Commit 6b348f07 authored by Sam Lantinga's avatar Sam Lantinga

Emphasized the separation between SDL_Surface and SDL_Texture

 - SDL_Surface is a system memory representation of pixel data
 - SDL_Texture is a video memory representation of pixel data

The concept of SDL_Surface with SDL_HWSURFACE is no longer used.

Separated SDL_Texture types by usage rather than memory type
 - SDL_TEXTUREACCESS_STATIC is for rarely changed pixel data,
   can be placed in video memory.
 - SDL_TEXTUREACCESS_STREAMING is for frequently changing pixel
   data, usually placed in system memory or AGP memory.

Optimized the SDL_compat usage of the OpenGL renderer by only
using one copy of the framebuffer instead of two.

--HG--
extra : convert_revision : svn%3Ac70aab31-4412-0410-b14c-859654838e24/trunk%402584
parent c1a200fa
...@@ -36,7 +36,7 @@ extern "C" { ...@@ -36,7 +36,7 @@ extern "C" {
/* *INDENT-ON* */ /* *INDENT-ON* */
#endif #endif
#define SDL_SWSURFACE 0x00000000 #define SDL_SWSURFACE 0x00000000 /* Not used */
#define SDL_ANYFORMAT 0x00100000 #define SDL_ANYFORMAT 0x00100000
#define SDL_HWPALETTE 0x00200000 #define SDL_HWPALETTE 0x00200000
#define SDL_DOUBLEBUF 0x00400000 #define SDL_DOUBLEBUF 0x00400000
...@@ -44,6 +44,7 @@ extern "C" { ...@@ -44,6 +44,7 @@ extern "C" {
#define SDL_RESIZABLE 0x01000000 #define SDL_RESIZABLE 0x01000000
#define SDL_NOFRAME 0x02000000 #define SDL_NOFRAME 0x02000000
#define SDL_OPENGL 0x04000000 #define SDL_OPENGL 0x04000000
#define SDL_HWSURFACE 0x08000001 /* Not used */
#define SDL_ASYNCBLIT 0x08000000 /* Not used */ #define SDL_ASYNCBLIT 0x08000000 /* Not used */
#define SDL_HWACCEL 0x08000000 /* Not used */ #define SDL_HWACCEL 0x08000000 /* Not used */
......
...@@ -204,8 +204,8 @@ typedef struct SDL_RendererInfo ...@@ -204,8 +204,8 @@ typedef struct SDL_RendererInfo
*/ */
typedef enum typedef enum
{ {
SDL_TEXTUREACCESS_LOCAL, /**< Lockable system memory */ SDL_TEXTUREACCESS_STATIC, /**< Changes rarely, not lockable */
SDL_TEXTUREACCESS_REMOTE /**< Unlockable video memory */ SDL_TEXTUREACCESS_STREAMING /**< Changes frequently, lockable */
} SDL_TextureAccess; } SDL_TextureAccess;
/** /**
...@@ -264,15 +264,14 @@ typedef void *SDL_GLContext; ...@@ -264,15 +264,14 @@ typedef void *SDL_GLContext;
/* These are the currently supported flags for the SDL_surface */ /* These are the currently supported flags for the SDL_surface */
/* Used internally (read-only) */ /* Used internally (read-only) */
#define SDL_HWSURFACE 0x00000001 /* Surface represents a texture */ #define SDL_PREALLOC 0x00000001 /* Surface uses preallocated memory */
#define SDL_PREALLOC 0x00000002 /* Surface uses preallocated memory */
#define SDL_SRCALPHA 0x00000004 /* Blit uses source alpha blending */ #define SDL_SRCALPHA 0x00000004 /* Blit uses source alpha blending */
#define SDL_SRCCOLORKEY 0x00000008 /* Blit uses a source color key */ #define SDL_SRCCOLORKEY 0x00000008 /* Blit uses a source color key */
#define SDL_RLEACCELOK 0x00000010 /* Private flag */ #define SDL_RLEACCELOK 0x00000010 /* Private flag */
#define SDL_RLEACCEL 0x00000020 /* Surface is RLE encoded */ #define SDL_RLEACCEL 0x00000020 /* Surface is RLE encoded */
/* Evaluates to true if the surface needs to be locked before access */ /* Evaluates to true if the surface needs to be locked before access */
#define SDL_MUSTLOCK(S) (((S)->flags & (SDL_HWSURFACE|SDL_RLEACCEL)) != 0) #define SDL_MUSTLOCK(S) (((S)->flags & SDL_RLEACCEL) != 0)
/* This structure should be treated as read-only, except for 'pixels', /* This structure should be treated as read-only, except for 'pixels',
which, if not NULL, contains the raw pixel data for the surface. which, if not NULL, contains the raw pixel data for the surface.
...@@ -288,9 +287,6 @@ typedef struct SDL_Surface ...@@ -288,9 +287,6 @@ typedef struct SDL_Surface
/* Application data associated with the surfade */ /* Application data associated with the surfade */
void *userdata; /* Read-write */ void *userdata; /* Read-write */
/* texture associated with the surface, if any */
SDL_TextureID textureID; /* Read-only */
/* information needed for surfaces requiring locks */ /* information needed for surfaces requiring locks */
int locked; /* Read-only */ int locked; /* Read-only */
void *lock_data; /* Read-only */ void *lock_data; /* Read-only */
...@@ -927,12 +923,11 @@ extern DECLSPEC SDL_TextureID SDLCALL SDL_CreateTexture(Uint32 format, ...@@ -927,12 +923,11 @@ extern DECLSPEC SDL_TextureID SDLCALL SDL_CreateTexture(Uint32 format,
int h); int h);
/** /**
* \fn SDL_TextureID SDL_CreateTextureFromSurface(Uint32 format, int access, SDL_Surface *surface) * \fn SDL_TextureID SDL_CreateTextureFromSurface(Uint32 format, SDL_Surface *surface)
* *
* \brief Create a texture from an existing surface. * \brief Create a texture from an existing surface.
* *
* \param format The format of the texture, or 0 to pick an appropriate format * \param format The format of the texture, or 0 to pick an appropriate format
* \param access One of the enumerated values in SDL_TextureAccess
* \param surface The surface containing pixel data used to fill the texture * \param surface The surface containing pixel data used to fill the texture
* *
* \return The created texture is returned, or 0 if no rendering context was active, the format was unsupported, or the surface width or height were out of range. * \return The created texture is returned, or 0 if no rendering context was active, the format was unsupported, or the surface width or height were out of range.
...@@ -944,7 +939,6 @@ extern DECLSPEC SDL_TextureID SDLCALL SDL_CreateTexture(Uint32 format, ...@@ -944,7 +939,6 @@ extern DECLSPEC SDL_TextureID SDLCALL SDL_CreateTexture(Uint32 format,
*/ */
extern DECLSPEC SDL_TextureID SDLCALL SDL_CreateTextureFromSurface(Uint32 extern DECLSPEC SDL_TextureID SDLCALL SDL_CreateTextureFromSurface(Uint32
format, format,
int access,
SDL_Surface SDL_Surface
* surface); * surface);
...@@ -970,7 +964,7 @@ extern DECLSPEC int SDLCALL SDL_QueryTexture(SDL_TextureID textureID, ...@@ -970,7 +964,7 @@ extern DECLSPEC int SDLCALL SDL_QueryTexture(SDL_TextureID textureID,
* *
* \brief Query the pixels of a texture, if the texture does not need to be locked for pixel access. * \brief Query the pixels of a texture, if the texture does not need to be locked for pixel access.
* *
* \param texture A texture to be queried, which was created with SDL_TEXTUREACCESS_LOCAL * \param texture A texture to be queried, which was created with SDL_TEXTUREACCESS_STREAMING
* \param pixels A pointer filled with a pointer to the pixels for the texture * \param pixels A pointer filled with a pointer to the pixels for the texture
* \param pitch A pointer filled in with the pitch of the pixel data * \param pitch A pointer filled in with the pitch of the pixel data
* *
...@@ -1155,7 +1149,7 @@ extern DECLSPEC int SDLCALL SDL_GetTextureScaleMode(SDL_TextureID textureID, ...@@ -1155,7 +1149,7 @@ extern DECLSPEC int SDLCALL SDL_GetTextureScaleMode(SDL_TextureID textureID,
* *
* \return 0 on success, or -1 if the texture is not valid * \return 0 on success, or -1 if the texture is not valid
* *
* \note This is a very slow function for textures not created with SDL_TEXTUREACCESS_LOCAL. * \note This is a fairly slow function.
*/ */
extern DECLSPEC int SDLCALL SDL_UpdateTexture(SDL_TextureID textureID, extern DECLSPEC int SDLCALL SDL_UpdateTexture(SDL_TextureID textureID,
const SDL_Rect * rect, const SDL_Rect * rect,
...@@ -1166,13 +1160,13 @@ extern DECLSPEC int SDLCALL SDL_UpdateTexture(SDL_TextureID textureID, ...@@ -1166,13 +1160,13 @@ extern DECLSPEC int SDLCALL SDL_UpdateTexture(SDL_TextureID textureID,
* *
* \brief Lock a portion of the texture for pixel access. * \brief Lock a portion of the texture for pixel access.
* *
* \param texture The texture to lock for access, which must have been created with SDL_TEXTUREACCESS_LOCAL. * \param textureID The texture to lock for access, which was created with SDL_TEXTUREACCESS_STREAMING.
* \param rect A pointer to the rectangle to lock for access. If the rect is NULL, the entire texture will be locked. * \param rect A pointer to the rectangle to lock for access. If the rect is NULL, the entire texture will be locked.
* \param markDirty If this is nonzero, the locked area will be marked dirty when the texture is unlocked. * \param markDirty If this is nonzero, the locked area will be marked dirty when the texture is unlocked.
* \param pixels This is filled in with a pointer to the locked pixels, appropriately offset by the locked area. * \param pixels This is filled in with a pointer to the locked pixels, appropriately offset by the locked area.
* \param pitch This is filled in with the pitch of the locked pixels. * \param pitch This is filled in with the pitch of the locked pixels.
* *
* \return 0 on success, or -1 if the texture is not valid or was created with SDL_TEXTUREACCESS_REMOTe * \return 0 on success, or -1 if the texture is not valid or was created with SDL_TEXTUREACCESS_STATIC
* *
* \sa SDL_DirtyTexture() * \sa SDL_DirtyTexture()
* \sa SDL_UnlockTexture() * \sa SDL_UnlockTexture()
...@@ -1197,7 +1191,9 @@ extern DECLSPEC void SDLCALL SDL_UnlockTexture(SDL_TextureID textureID); ...@@ -1197,7 +1191,9 @@ extern DECLSPEC void SDLCALL SDL_UnlockTexture(SDL_TextureID textureID);
* *
* \brief Mark the specified rectangles of the texture as dirty. * \brief Mark the specified rectangles of the texture as dirty.
* *
* \note The texture must have been created with SDL_TEXTUREACCESS_LOCAL. * \param textureID The texture to mark dirty, which was created with SDL_TEXTUREACCESS_STREAMING.
* \param numrects The number of rectangles pointed to by rects.
* \param rects The pointer to an array of dirty rectangles.
* *
* \sa SDL_LockTexture() * \sa SDL_LockTexture()
* \sa SDL_UnlockTexture() * \sa SDL_UnlockTexture()
...@@ -1347,8 +1343,6 @@ extern DECLSPEC SDL_Surface *SDLCALL SDL_CreateRGBSurfaceFrom(void *pixels, ...@@ -1347,8 +1343,6 @@ extern DECLSPEC SDL_Surface *SDLCALL SDL_CreateRGBSurfaceFrom(void *pixels,
Uint32 Gmask, Uint32 Gmask,
Uint32 Bmask, Uint32 Bmask,
Uint32 Amask); Uint32 Amask);
extern DECLSPEC SDL_Surface *SDLCALL
SDL_CreateRGBSurfaceFromTexture(SDL_TextureID textureID);
extern DECLSPEC void SDLCALL SDL_FreeSurface(SDL_Surface * surface); extern DECLSPEC void SDLCALL SDL_FreeSurface(SDL_Surface * surface);
/** /**
......
...@@ -328,6 +328,38 @@ GetEnvironmentWindowPosition(int w, int h, int *x, int *y) ...@@ -328,6 +328,38 @@ GetEnvironmentWindowPosition(int w, int h, int *x, int *y)
} }
} }
static SDL_Surface *
CreateVideoSurface(SDL_TextureID textureID)
{
SDL_Surface *surface;
Uint32 format;
int w, h;
int bpp;
Uint32 Rmask, Gmask, Bmask, Amask;
void *pixels;
int pitch;
if (SDL_QueryTexture(textureID, &format, NULL, &w, &h) < 0) {
return NULL;
}
if (!SDL_PixelFormatEnumToMasks
(format, &bpp, &Rmask, &Gmask, &Bmask, &Amask)) {
SDL_SetError("Unknown texture format");
return NULL;
}
if (SDL_QueryTexturePixels(textureID, &pixels, &pitch) == 0) {
surface =
SDL_CreateRGBSurfaceFrom(pixels, w, h, bpp, pitch, Rmask, Gmask,
Bmask, Amask);
} else {
surface =
SDL_CreateRGBSurface(0, w, h, bpp, Rmask, Gmask, Bmask, Amask);
}
return surface;
}
SDL_Surface * SDL_Surface *
SDL_SetVideoMode(int width, int height, int bpp, Uint32 flags) SDL_SetVideoMode(int width, int height, int bpp, Uint32 flags)
{ {
...@@ -483,23 +515,23 @@ SDL_SetVideoMode(int width, int height, int bpp, Uint32 flags) ...@@ -483,23 +515,23 @@ SDL_SetVideoMode(int width, int height, int bpp, Uint32 flags)
SDL_RENDERER_SINGLEBUFFER | SDL_RENDERER_PRESENTDISCARD) < 0) { SDL_RENDERER_SINGLEBUFFER | SDL_RENDERER_PRESENTDISCARD) < 0) {
return NULL; return NULL;
} }
SDL_GetRenderDriverInfo(-1, &SDL_VideoRendererInfo); SDL_GetRendererInfo(&SDL_VideoRendererInfo);
/* Create a texture for the screen surface */ /* Create a texture for the screen surface */
SDL_VideoTexture = SDL_VideoTexture =
SDL_CreateTexture(desired_format, SDL_TEXTUREACCESS_LOCAL, width, SDL_CreateTexture(desired_format, SDL_TEXTUREACCESS_STREAMING, width,
height); height);
if (!SDL_VideoTexture) { if (!SDL_VideoTexture) {
SDL_VideoTexture = SDL_VideoTexture =
SDL_CreateTexture(SDL_PIXELFORMAT_RGB888, SDL_TEXTUREACCESS_LOCAL, SDL_CreateTexture(SDL_PIXELFORMAT_RGB888,
width, height); SDL_TEXTUREACCESS_STREAMING, width, height);
} }
if (!SDL_VideoTexture) { if (!SDL_VideoTexture) {
return NULL; return NULL;
} }
/* Create the screen surface */ /* Create the screen surface */
SDL_VideoSurface = SDL_CreateRGBSurfaceFromTexture(SDL_VideoTexture); SDL_VideoSurface = CreateVideoSurface(SDL_VideoTexture);
if (!SDL_VideoSurface) { if (!SDL_VideoSurface) {
return NULL; return NULL;
} }
...@@ -518,23 +550,10 @@ SDL_SetVideoMode(int width, int height, int bpp, Uint32 flags) ...@@ -518,23 +550,10 @@ SDL_SetVideoMode(int width, int height, int bpp, Uint32 flags)
} }
/* Create a shadow surface if necessary */ /* Create a shadow surface if necessary */
if (((bpp != SDL_VideoSurface->format->BitsPerPixel) if ((bpp != SDL_VideoSurface->format->BitsPerPixel)
&& !(flags & SDL_ANYFORMAT)) && !(flags & SDL_ANYFORMAT)) {
|| ((SDL_VideoSurface->flags & SDL_HWSURFACE) SDL_ShadowSurface =
&& !(flags & SDL_HWSURFACE))) { SDL_CreateRGBSurface(0, width, height, bpp, 0, 0, 0, 0);
if ((bpp == SDL_VideoSurface->format->BitsPerPixel)
|| (flags & SDL_ANYFORMAT)) {
SDL_ShadowSurface =
SDL_CreateRGBSurface(0, width, height,
SDL_VideoSurface->format->BitsPerPixel,
SDL_VideoSurface->format->Rmask,
SDL_VideoSurface->format->Gmask,
SDL_VideoSurface->format->Bmask,
SDL_VideoSurface->format->Amask);
} else {
SDL_ShadowSurface =
SDL_CreateRGBSurface(0, width, height, bpp, 0, 0, 0, 0);
}
if (!SDL_ShadowSurface) { if (!SDL_ShadowSurface) {
return NULL; return NULL;
} }
...@@ -638,8 +657,7 @@ SDL_DisplayFormatAlpha(SDL_Surface * surface) ...@@ -638,8 +657,7 @@ SDL_DisplayFormatAlpha(SDL_Surface * surface)
break; break;
} }
format = SDL_AllocFormat(32, rmask, gmask, bmask, amask); format = SDL_AllocFormat(32, rmask, gmask, bmask, amask);
flags = SDL_PublicSurface->flags & SDL_HWSURFACE; flags = surface->flags & (SDL_SRCALPHA | SDL_RLEACCELOK);
flags |= surface->flags & (SDL_SRCALPHA | SDL_RLEACCELOK);
converted = SDL_ConvertSurface(surface, format, flags); converted = SDL_ConvertSurface(surface, format, flags);
SDL_FreeFormat(format); SDL_FreeFormat(format);
return converted; return converted;
...@@ -681,6 +699,22 @@ SDL_UpdateRects(SDL_Surface * screen, int numrects, SDL_Rect * rects) ...@@ -681,6 +699,22 @@ SDL_UpdateRects(SDL_Surface * screen, int numrects, SDL_Rect * rects)
screen = SDL_VideoSurface; screen = SDL_VideoSurface;
} }
if (screen == SDL_VideoSurface) { if (screen == SDL_VideoSurface) {
if (screen->flags & SDL_PREALLOC) {
/* The surface memory is maintained by the renderer */
SDL_DirtyTexture(SDL_VideoTexture, numrects, rects);
} else {
/* The surface memory needs to be copied to texture */
void *pixels;
int pitch = screen->pitch;
int psize = screen->format->BytesPerPixel;
for (i = 0; i < numrects; ++i) {
const SDL_Rect *rect = &rects[i];
void *pixels =
(Uint8 *) screen->pixels + rect->y * pitch +
rect->x * psize;
SDL_UpdateTexture(SDL_VideoTexture, rect, pixels, pitch);
}
}
if (SDL_VideoRendererInfo.flags & SDL_RENDERER_PRESENTCOPY) { if (SDL_VideoRendererInfo.flags & SDL_RENDERER_PRESENTCOPY) {
for (i = 0; i < numrects; ++i) { for (i = 0; i < numrects; ++i) {
SDL_RenderCopy(SDL_VideoTexture, &rects[i], &rects[i]); SDL_RenderCopy(SDL_VideoTexture, &rects[i], &rects[i]);
...@@ -1174,8 +1208,7 @@ SDL_DrawCursor(SDL_Surface * screen) ...@@ -1174,8 +1208,7 @@ SDL_DrawCursor(SDL_Surface * screen)
if (SDL_MUSTLOCK(screen)) { if (SDL_MUSTLOCK(screen)) {
SDL_UnlockSurface(screen); SDL_UnlockSurface(screen);
} }
if ((screen->flags & SDL_SCREEN_SURFACE) && if (screen->flags & SDL_SCREEN_SURFACE) {
!(screen->flags & SDL_HWSURFACE)) {
SDL_VideoDevice *_this = SDL_GetVideoDevice(); SDL_VideoDevice *_this = SDL_GetVideoDevice();
SDL_Window *window; SDL_Window *window;
SDL_Rect area; SDL_Rect area;
...@@ -1263,8 +1296,7 @@ SDL_EraseCursor(SDL_Surface * screen) ...@@ -1263,8 +1296,7 @@ SDL_EraseCursor(SDL_Surface * screen)
if (SDL_MUSTLOCK(screen)) { if (SDL_MUSTLOCK(screen)) {
SDL_UnlockSurface(screen); SDL_UnlockSurface(screen);
} }
if ((screen->flags & SDL_SCREEN_SURFACE) && if (screen->flags & SDL_SCREEN_SURFACE) {
!(screen->flags & SDL_HWSURFACE)) {
SDL_VideoDevice *_this = SDL_GetVideoDevice(); SDL_VideoDevice *_this = SDL_GetVideoDevice();
SDL_Window *window; SDL_Window *window;
SDL_Rect area; SDL_Rect area;
...@@ -1385,7 +1417,7 @@ SDL_CreateYUVOverlay(int w, int h, Uint32 format, SDL_Surface * display) ...@@ -1385,7 +1417,7 @@ SDL_CreateYUVOverlay(int w, int h, Uint32 format, SDL_Surface * display)
} }
overlay->hwdata->textureID = overlay->hwdata->textureID =
SDL_CreateTexture(texture_format, SDL_TEXTUREACCESS_LOCAL, w, h); SDL_CreateTexture(texture_format, SDL_TEXTUREACCESS_STREAMING, w, h);
if (!overlay->hwdata->textureID) { if (!overlay->hwdata->textureID) {
SDL_FreeYUVOverlay(overlay); SDL_FreeYUVOverlay(overlay);
return NULL; return NULL;
......
...@@ -1618,7 +1618,7 @@ RLEAlphaSurface(SDL_Surface * surface) ...@@ -1618,7 +1618,7 @@ RLEAlphaSurface(SDL_Surface * surface)
#undef ADD_TRANSL_COUNTS #undef ADD_TRANSL_COUNTS
/* Now that we have it encoded, release the original pixels */ /* Now that we have it encoded, release the original pixels */
if (!(surface->flags & SDL_PREALLOC) && !(surface->flags & SDL_HWSURFACE)) { if (!(surface->flags & SDL_PREALLOC)) {
SDL_free(surface->pixels); SDL_free(surface->pixels);
surface->pixels = NULL; surface->pixels = NULL;
} }
...@@ -1783,7 +1783,7 @@ RLEColorkeySurface(SDL_Surface * surface) ...@@ -1783,7 +1783,7 @@ RLEColorkeySurface(SDL_Surface * surface)
#undef ADD_COUNTS #undef ADD_COUNTS
/* Now that we have it encoded, release the original pixels */ /* Now that we have it encoded, release the original pixels */
if (!(surface->flags & SDL_PREALLOC) && !(surface->flags & SDL_HWSURFACE)) { if (!(surface->flags & SDL_PREALLOC)) {
SDL_free(surface->pixels); SDL_free(surface->pixels);
surface->pixels = NULL; surface->pixels = NULL;
} }
...@@ -1934,8 +1934,7 @@ SDL_UnRLESurface(SDL_Surface * surface, int recode) ...@@ -1934,8 +1934,7 @@ SDL_UnRLESurface(SDL_Surface * surface, int recode)
if ((surface->flags & SDL_RLEACCEL) == SDL_RLEACCEL) { if ((surface->flags & SDL_RLEACCEL) == SDL_RLEACCEL) {
surface->flags &= ~SDL_RLEACCEL; surface->flags &= ~SDL_RLEACCEL;
if (recode && !(surface->flags & SDL_PREALLOC) if (recode && !(surface->flags & SDL_PREALLOC)) {
&& !(surface->flags & SDL_HWSURFACE)) {
if ((surface->flags & SDL_SRCCOLORKEY) == SDL_SRCCOLORKEY) { if ((surface->flags & SDL_SRCCOLORKEY) == SDL_SRCCOLORKEY) {
SDL_Rect full; SDL_Rect full;
unsigned alpha_flag; unsigned alpha_flag;
......
...@@ -38,6 +38,9 @@ static SDL_Renderer *GL_CreateRenderer(SDL_Window * window, Uint32 flags); ...@@ -38,6 +38,9 @@ static SDL_Renderer *GL_CreateRenderer(SDL_Window * window, Uint32 flags);
static int GL_ActivateRenderer(SDL_Renderer * renderer); static int GL_ActivateRenderer(SDL_Renderer * renderer);
static int GL_DisplayModeChanged(SDL_Renderer * renderer); static int GL_DisplayModeChanged(SDL_Renderer * renderer);
static int GL_CreateTexture(SDL_Renderer * renderer, SDL_Texture * texture); static int GL_CreateTexture(SDL_Renderer * renderer, SDL_Texture * texture);
static int GL_QueryTexturePixels(SDL_Renderer * renderer,
SDL_Texture * texture, void **pixels,
int *pitch);
static int GL_SetTexturePalette(SDL_Renderer * renderer, static int GL_SetTexturePalette(SDL_Renderer * renderer,
SDL_Texture * texture, SDL_Texture * texture,
const SDL_Color * colors, int firstcolor, const SDL_Color * colors, int firstcolor,
...@@ -245,6 +248,7 @@ GL_CreateRenderer(SDL_Window * window, Uint32 flags) ...@@ -245,6 +248,7 @@ GL_CreateRenderer(SDL_Window * window, Uint32 flags)
renderer->ActivateRenderer = GL_ActivateRenderer; renderer->ActivateRenderer = GL_ActivateRenderer;
renderer->DisplayModeChanged = GL_DisplayModeChanged; renderer->DisplayModeChanged = GL_DisplayModeChanged;
renderer->CreateTexture = GL_CreateTexture; renderer->CreateTexture = GL_CreateTexture;
renderer->QueryTexturePixels = GL_QueryTexturePixels;
renderer->SetTexturePalette = GL_SetTexturePalette; renderer->SetTexturePalette = GL_SetTexturePalette;
renderer->GetTexturePalette = GL_GetTexturePalette; renderer->GetTexturePalette = GL_GetTexturePalette;
renderer->SetTextureColorMod = GL_SetTextureColorMod; renderer->SetTextureColorMod = GL_SetTextureColorMod;
...@@ -492,6 +496,16 @@ GL_CreateTexture(SDL_Renderer * renderer, SDL_Texture * texture) ...@@ -492,6 +496,16 @@ GL_CreateTexture(SDL_Renderer * renderer, SDL_Texture * texture)
SDL_memset(data->palette, 0xFF, 3 * 256 * sizeof(Uint8)); SDL_memset(data->palette, 0xFF, 3 * 256 * sizeof(Uint8));
} }
if (texture->access == SDL_TEXTUREACCESS_STREAMING) {
data->pitch = texture->w * SDL_BYTESPERPIXEL(texture->format);
data->pixels = SDL_malloc(texture->h * data->pitch);
if (!data->pixels) {
SDL_OutOfMemory();
SDL_free(data);
return -1;
}
}
texture->driverdata = data; texture->driverdata = data;
renderdata->glGetError(); renderdata->glGetError();
...@@ -522,6 +536,17 @@ GL_CreateTexture(SDL_Renderer * renderer, SDL_Texture * texture) ...@@ -522,6 +536,17 @@ GL_CreateTexture(SDL_Renderer * renderer, SDL_Texture * texture)
return 0; return 0;
} }
static int
GL_QueryTexturePixels(SDL_Renderer * renderer, SDL_Texture * texture,
void **pixels, int *pitch)
{
GL_TextureData *data = (GL_TextureData *) texture->driverdata;
*pixels = data->pixels;
*pitch = data->pitch;
return 0;
}
static int static int
GL_SetTexturePalette(SDL_Renderer * renderer, SDL_Texture * texture, GL_SetTexturePalette(SDL_Renderer * renderer, SDL_Texture * texture,
const SDL_Color * colors, int firstcolor, int ncolors) const SDL_Color * colors, int firstcolor, int ncolors)
...@@ -661,15 +686,6 @@ GL_LockTexture(SDL_Renderer * renderer, SDL_Texture * texture, ...@@ -661,15 +686,6 @@ GL_LockTexture(SDL_Renderer * renderer, SDL_Texture * texture,
{ {
GL_TextureData *data = (GL_TextureData *) texture->driverdata; GL_TextureData *data = (GL_TextureData *) texture->driverdata;
if (!data->pixels) {
data->pitch = texture->w * SDL_BYTESPERPIXEL(texture->format);
data->pixels = SDL_malloc(texture->h * data->pitch);
if (!data->pixels) {
SDL_OutOfMemory();
return -1;
}
}
if (markDirty) { if (markDirty) {
SDL_AddDirtyRect(&data->dirty, rect); SDL_AddDirtyRect(&data->dirty, rect);
} }
......
...@@ -125,7 +125,7 @@ CreateTexture(SDL_Renderer * renderer, Uint32 format, int w, int h) ...@@ -125,7 +125,7 @@ CreateTexture(SDL_Renderer * renderer, Uint32 format, int w, int h)
} }
texture->format = format; texture->format = format;
texture->access = SDL_TEXTUREACCESS_LOCAL; texture->access = SDL_TEXTUREACCESS_STREAMING;
texture->w = w; texture->w = w;
texture->h = h; texture->h = h;
texture->renderer = renderer; texture->renderer = renderer;
......
...@@ -170,62 +170,11 @@ SDL_CreateRGBSurfaceFrom(void *pixels, ...@@ -170,62 +170,11 @@ SDL_CreateRGBSurfaceFrom(void *pixels,
return surface; return surface;
} }
SDL_Surface *
SDL_CreateRGBSurfaceFromTexture(SDL_TextureID textureID)
{
SDL_Surface *surface;
Uint32 format;
int w, h;
int bpp;
Uint32 Rmask, Gmask, Bmask, Amask;
void *pixels;
int pitch;
if (SDL_QueryTexture(textureID, &format, NULL, &w, &h) < 0) {
return NULL;
}
if (!SDL_PixelFormatEnumToMasks
(format, &bpp, &Rmask, &Gmask, &Bmask, &Amask)) {
SDL_SetError("Unknown texture format");
return NULL;
}
if (SDL_QueryTexturePixels(textureID, &pixels, &pitch) == 0) {
surface =
SDL_CreateRGBSurfaceFrom(pixels, w, h, bpp, pitch, Rmask, Gmask,
Bmask, Amask);
} else {
surface =
SDL_CreateRGBSurface(0, 0, 0, bpp, Rmask, Gmask, Bmask, Amask);
if (surface) {
surface->flags |= SDL_HWSURFACE;
surface->w = w;
surface->h = h;
surface->pitch = SDL_CalculatePitch(surface);
SDL_SetClipRect(surface, NULL);
}
}
if (surface) {
surface->textureID = textureID;
}
return surface;
}
static int static int
SDL_SurfacePaletteChanged(void *userdata, SDL_Palette * palette) SDL_SurfacePaletteChanged(void *userdata, SDL_Palette * palette)
{ {
SDL_Surface *surface = (SDL_Surface *) userdata; SDL_Surface *surface = (SDL_Surface *) userdata;
if (surface->textureID) {
if (SDL_SetTexturePalette
(surface->textureID, palette->colors, 0, palette->ncolors) < 0) {
SDL_GetTexturePalette(surface->textureID, palette->colors, 0,
palette->ncolors);
return -1;
}
}
SDL_FormatChanged(surface); SDL_FormatChanged(surface);
return 0; return 0;
...@@ -627,74 +576,9 @@ SDL_FillRect(SDL_Surface * dst, SDL_Rect * dstrect, Uint32 color) ...@@ -627,74 +576,9 @@ SDL_FillRect(SDL_Surface * dst, SDL_Rect * dstrect, Uint32 color)
row += dst->pitch; row += dst->pitch;
} }
} else { } else {
#ifdef __powerpc__ for (y = dstrect->h; y; y--) {
/* SDL_memset(row, color, x);
* SDL_memset() on PPC (both glibc and codewarrior) uses row += dst->pitch;
* the dcbz (Data Cache Block Zero) instruction, which
* causes an alignment exception if the destination is
* uncachable, so only use it on software surfaces
*/
if (dst->flags & SDL_HWSURFACE) {
if (dstrect->w >= 8) {
/*
* 64-bit stores are probably most
* efficient to uncached video memory
*/
double fill;
SDL_memset(&fill, color, (sizeof fill));
for (y = dstrect->h; y; y--) {
Uint8 *d = row;
unsigned n = x;
unsigned nn;
Uint8 c = color;
double f = fill;
while ((unsigned long) d & (sizeof(double) - 1)) {
*d++ = c;
n--;
}
nn = n / (sizeof(double) * 4);
while (nn) {
((double *) d)[0] = f;
((double *) d)[1] = f;
((double *) d)[2] = f;
((double *) d)[3] = f;
d += 4 * sizeof(double);
nn--;
}
n &= ~(sizeof(double) * 4 - 1);
nn = n / sizeof(double);
while (nn) {
*(double *) d = f;
d += sizeof(double);
nn--;
}
n &= ~(sizeof(double) - 1);
while (n) {
*d++ = c;
n--;
}
row += dst->pitch;
}
} else {
/* narrow boxes */
for (y = dstrect->h; y; y--) {
Uint8 *d = row;
Uint8 c = color;
int n = x;
while (n) {
*d++ = c;
n--;
}
row += dst->pitch;
}
}
} else
#endif /* __powerpc__ */
{
for (y = dstrect->h; y; y--) {
SDL_memset(row, color, x);
row += dst->pitch;
}
} }
} }
} else { } else {
...@@ -753,13 +637,6 @@ SDL_LockSurface(SDL_Surface * surface) ...@@ -753,13 +637,6 @@ SDL_LockSurface(SDL_Surface * surface)
{ {
if (!surface->locked) { if (!surface->locked) {
/* Perform the lock */ /* Perform the lock */
if (surface->flags & SDL_HWSURFACE) {
if (SDL_LockTexture
(surface->textureID, NULL, 1, &surface->pixels,
&surface->pitch) < 0) {
return (-1);
}
}
if (surface->flags & SDL_RLEACCEL) { if (surface->flags & SDL_RLEACCEL) {
SDL_UnRLESurface(surface, 1); SDL_UnRLESurface(surface, 1);
surface->flags |= SDL_RLEACCEL; /* save accel'd state */ surface->flags |= SDL_RLEACCEL; /* save accel'd state */
...@@ -784,11 +661,6 @@ SDL_UnlockSurface(SDL_Surface * surface) ...@@ -784,11 +661,6 @@ SDL_UnlockSurface(SDL_Surface * surface)
return; return;
} }
/* Unlock hardware or accelerated surfaces */
if (surface->flags & SDL_HWSURFACE) {
SDL_UnlockTexture(surface->textureID);
}
/* Update RLE encoded surface with new data */ /* Update RLE encoded surface with new data */
if ((surface->flags & SDL_RLEACCEL) == SDL_RLEACCEL) { if ((surface->flags & SDL_RLEACCEL) == SDL_RLEACCEL) {
surface->flags &= ~SDL_RLEACCEL; /* stop lying */ surface->flags &= ~SDL_RLEACCEL; /* stop lying */
...@@ -928,11 +800,6 @@ SDL_FreeSurface(SDL_Surface * surface) ...@@ -928,11 +800,6 @@ SDL_FreeSurface(SDL_Surface * surface)
SDL_FreeBlitMap(surface->map); SDL_FreeBlitMap(surface->map);
surface->map = NULL; surface->map = NULL;
} }
/* Should we destroy the texture too?
if (surface->textureID) {
SDL_DestroyTexture(surface->textureID);
}
*/
if (surface->pixels && ((surface->flags & SDL_PREALLOC) != SDL_PREALLOC)) { if (surface->pixels && ((surface->flags & SDL_PREALLOC) != SDL_PREALLOC)) {
SDL_free(surface->pixels); SDL_free(surface->pixels);
} }
......
...@@ -1532,14 +1532,11 @@ SDL_CreateTexture(Uint32 format, int access, int w, int h) ...@@ -1532,14 +1532,11 @@ SDL_CreateTexture(Uint32 format, int access, int w, int h)
} }
SDL_TextureID SDL_TextureID
SDL_CreateTextureFromSurface(Uint32 format, int access, SDL_Surface * surface) SDL_CreateTextureFromSurface(Uint32 format, SDL_Surface * surface)
{ {
SDL_TextureID textureID; SDL_TextureID textureID;
Uint32 surface_flags = surface->flags; Uint32 surface_flags = surface->flags;
SDL_PixelFormat *fmt = surface->format; SDL_PixelFormat *fmt = surface->format;
Uint8 alpha;
SDL_Rect bounds;
SDL_Surface dst;
int bpp; int bpp;
Uint32 Rmask, Gmask, Bmask, Amask; Uint32 Rmask, Gmask, Bmask, Amask;
...@@ -1576,23 +1573,41 @@ SDL_CreateTextureFromSurface(Uint32 format, int access, SDL_Surface * surface) ...@@ -1576,23 +1573,41 @@ SDL_CreateTextureFromSurface(Uint32 format, int access, SDL_Surface * surface)
} }
} }
textureID = SDL_CreateTexture(format, access, surface->w, surface->h); textureID =
SDL_CreateTexture(format, SDL_TEXTUREACCESS_STATIC, surface->w,
surface->h);
if (!textureID) { if (!textureID) {
return 0; return 0;
} }
/* Set up a destination surface for the texture update */ if (bpp == fmt->BitsPerPixel && Rmask == fmt->Rmask && Gmask == fmt->Gmask
SDL_zero(dst); && Bmask == fmt->Bmask && Amask == fmt->Amask) {
dst.format = SDL_AllocFormat(bpp, Rmask, Gmask, Bmask, Amask); if (SDL_MUSTLOCK(surface)) {
if (!dst.format) { if (SDL_LockSurface(surface) < 0) {
SDL_DestroyTexture(textureID); SDL_DestroyTexture(textureID);
return 0; return 0;
} }
dst.w = surface->w; SDL_UpdateTexture(textureID, NULL, surface->pixels,
dst.h = surface->h; surface->pitch);
if (SDL_LockTexture(textureID, NULL, 1, &dst.pixels, &dst.pitch) == 0) { SDL_UnlockSurface(surface);
dst.flags |= SDL_PREALLOC; } else {
SDL_UpdateTexture(textureID, NULL, surface->pixels,
surface->pitch);
}
} else { } else {
Uint8 alpha;
SDL_Rect bounds;
SDL_Surface dst;
/* Set up a destination surface for the texture update */
SDL_zero(dst);
dst.format = SDL_AllocFormat(bpp, Rmask, Gmask, Bmask, Amask);
if (!dst.format) {
SDL_DestroyTexture(textureID);
return 0;
}
dst.w = surface->w;
dst.h = surface->h;
dst.pitch = SDL_CalculatePitch(&dst); dst.pitch = SDL_CalculatePitch(&dst);
dst.pixels = SDL_malloc(dst.h * dst.pitch); dst.pixels = SDL_malloc(dst.h * dst.pitch);
if (!dst.pixels) { if (!dst.pixels) {
...@@ -1601,76 +1616,72 @@ SDL_CreateTextureFromSurface(Uint32 format, int access, SDL_Surface * surface) ...@@ -1601,76 +1616,72 @@ SDL_CreateTextureFromSurface(Uint32 format, int access, SDL_Surface * surface)
SDL_OutOfMemory(); SDL_OutOfMemory();
return 0; return 0;
} }
}
/* Copy the palette if any */ /* Copy the palette if any */
if (SDL_ISPIXELFORMAT_INDEXED(format)) { if (SDL_ISPIXELFORMAT_INDEXED(format)) {
if (fmt->palette) { if (fmt->palette) {
SDL_SetTexturePalette(textureID, fmt->palette->colors, 0, SDL_SetTexturePalette(textureID, fmt->palette->colors, 0,
fmt->palette->ncolors); fmt->palette->ncolors);
SDL_SetSurfacePalette(&dst, fmt->palette); SDL_SetSurfacePalette(&dst, fmt->palette);
} else { } else {
dst.format->palette = dst.format->palette =
SDL_AllocPalette((1 << SDL_BITSPERPIXEL(format))); SDL_AllocPalette((1 << SDL_BITSPERPIXEL(format)));
if (!dst.format->palette) { if (!dst.format->palette) {
SDL_DestroyTexture(textureID); SDL_DestroyTexture(textureID);
SDL_FreeFormat(dst.format); SDL_FreeFormat(dst.format);
return 0; return 0;
}
SDL_DitherColors(dst.format->palette->colors,
SDL_BITSPERPIXEL(format));
} }
SDL_DitherColors(dst.format->palette->colors,
SDL_BITSPERPIXEL(format));
} }
}
/* Make the texture transparent if the surface has colorkey */ /* Make the texture transparent if the surface has colorkey */
if (surface_flags & SDL_SRCCOLORKEY) { if (surface_flags & SDL_SRCCOLORKEY) {
int row; int row;
int length = dst.w * dst.format->BytesPerPixel; int length = dst.w * dst.format->BytesPerPixel;
Uint8 *p = (Uint8 *) dst.pixels; Uint8 *p = (Uint8 *) dst.pixels;
for (row = 0; row < dst.h; ++row) { for (row = 0; row < dst.h; ++row) {
SDL_memset(p, 0, length); SDL_memset(p, 0, length);
p += dst.pitch; p += dst.pitch;
}
} }
}
/* Copy over the alpha channel */ /* Copy over the alpha channel */
if (surface_flags & SDL_SRCALPHA) { if (surface_flags & SDL_SRCALPHA) {
if (fmt->Amask) { if (fmt->Amask) {
surface->flags &= ~SDL_SRCALPHA; surface->flags &= ~SDL_SRCALPHA;
} else { } else {
/* FIXME: Need to make sure the texture has an alpha channel /* FIXME: Need to make sure the texture has an alpha channel
* and copy 'alpha' into the texture alpha channel. * and copy 'alpha' into the texture alpha channel.
*/ */
alpha = surface->format->alpha; alpha = surface->format->alpha;
SDL_SetAlpha(surface, 0, 0); SDL_SetAlpha(surface, 0, 0);
} }
}
/* Copy over the image data */
bounds.x = 0;
bounds.y = 0;
bounds.w = surface->w;
bounds.h = surface->h;
SDL_LowerBlit(surface, &bounds, &dst, &bounds);
/* Clean up the original surface */
if ((surface_flags & SDL_SRCALPHA) == SDL_SRCALPHA) {
Uint32 aflags = surface_flags & (SDL_SRCALPHA | SDL_RLEACCELOK);
if (fmt->Amask) {
surface->flags |= SDL_SRCALPHA;
} else {
SDL_SetAlpha(surface, aflags, alpha);
} }
}
/* Update the texture */ /* Copy over the image data */
if (dst.flags & SDL_PREALLOC) { bounds.x = 0;
SDL_UnlockTexture(textureID); bounds.y = 0;
} else { bounds.w = surface->w;
bounds.h = surface->h;
SDL_LowerBlit(surface, &bounds, &dst, &bounds);
/* Clean up the original surface */
if ((surface_flags & SDL_SRCALPHA) == SDL_SRCALPHA) {
Uint32 aflags = surface_flags & (SDL_SRCALPHA | SDL_RLEACCELOK);
if (fmt->Amask) {
surface->flags |= SDL_SRCALPHA;
} else {
SDL_SetAlpha(surface, aflags, alpha);
}
}
/* Update the texture */
SDL_UpdateTexture(textureID, NULL, dst.pixels, dst.pitch); SDL_UpdateTexture(textureID, NULL, dst.pixels, dst.pitch);
SDL_free(dst.pixels); SDL_free(dst.pixels);
SDL_FreeFormat(dst.format);
} }
SDL_FreeFormat(dst.format);
return textureID; return textureID;
} }
...@@ -1967,6 +1978,10 @@ SDL_LockTexture(SDL_TextureID textureID, const SDL_Rect * rect, int markDirty, ...@@ -1967,6 +1978,10 @@ SDL_LockTexture(SDL_TextureID textureID, const SDL_Rect * rect, int markDirty,
if (!texture) { if (!texture) {
return -1; return -1;
} }
if (texture->access != SDL_TEXTUREACCESS_STREAMING) {
SDL_SetError("SDL_LockTexture(): texture must be streaming");
return -1;
}
renderer = texture->renderer; renderer = texture->renderer;
if (!renderer->LockTexture) { if (!renderer->LockTexture) {
...@@ -1994,6 +2009,9 @@ SDL_UnlockTexture(SDL_TextureID textureID) ...@@ -1994,6 +2009,9 @@ SDL_UnlockTexture(SDL_TextureID textureID)
if (!texture) { if (!texture) {
return; return;
} }
if (texture->access != SDL_TEXTUREACCESS_STREAMING) {
return;
}
renderer = texture->renderer; renderer = texture->renderer;
if (!renderer->UnlockTexture) { if (!renderer->UnlockTexture) {
...@@ -2012,6 +2030,9 @@ SDL_DirtyTexture(SDL_TextureID textureID, int numrects, ...@@ -2012,6 +2030,9 @@ SDL_DirtyTexture(SDL_TextureID textureID, int numrects,
if (!texture) { if (!texture) {
return; return;
} }
if (texture->access != SDL_TEXTUREACCESS_STREAMING) {
return;
}
renderer = texture->renderer; renderer = texture->renderer;
if (!renderer->DirtyTexture) { if (!renderer->DirtyTexture) {
......
...@@ -459,7 +459,6 @@ D3D_CreateTexture(SDL_Renderer * renderer, SDL_Texture * texture) ...@@ -459,7 +459,6 @@ D3D_CreateTexture(SDL_Renderer * renderer, SDL_Texture * texture)
SDL_Window *window = SDL_GetWindowFromID(renderer->window); SDL_Window *window = SDL_GetWindowFromID(renderer->window);
SDL_VideoDisplay *display = SDL_GetDisplayFromWindow(window); SDL_VideoDisplay *display = SDL_GetDisplayFromWindow(window);
D3D_TextureData *data; D3D_TextureData *data;
D3DPOOL pool;
HRESULT result; HRESULT result;
data = (D3D_TextureData *) SDL_calloc(1, sizeof(*data)); data = (D3D_TextureData *) SDL_calloc(1, sizeof(*data));
...@@ -470,22 +469,11 @@ D3D_CreateTexture(SDL_Renderer * renderer, SDL_Texture * texture) ...@@ -470,22 +469,11 @@ D3D_CreateTexture(SDL_Renderer * renderer, SDL_Texture * texture)
texture->driverdata = data; texture->driverdata = data;
#if 1
/* FIXME: Do we want non-managed textures?
They need to be freed on device reset and then reloaded by the app...
*/
texture->access = SDL_TEXTUREACCESS_LOCAL;
#endif
if (texture->access == SDL_TEXTUREACCESS_LOCAL) {
pool = D3DPOOL_MANAGED;
} else {
pool = D3DPOOL_DEFAULT;
}
result = result =
IDirect3DDevice9_CreateTexture(renderdata->device, texture->w, IDirect3DDevice9_CreateTexture(renderdata->device, texture->w,
texture->h, 1, 0, texture->h, 1, 0,
PixelFormatToD3DFMT(texture->format), PixelFormatToD3DFMT(texture->format),
pool, &data->texture, NULL); D3DPOOL_MANAGED, &data->texture, NULL);
if (FAILED(result)) { if (FAILED(result)) {
D3D_SetError("CreateTexture()", result); D3D_SetError("CreateTexture()", result);
return -1; return -1;
...@@ -628,11 +616,6 @@ D3D_LockTexture(SDL_Renderer * renderer, SDL_Texture * texture, ...@@ -628,11 +616,6 @@ D3D_LockTexture(SDL_Renderer * renderer, SDL_Texture * texture,
D3DLOCKED_RECT locked; D3DLOCKED_RECT locked;
HRESULT result; HRESULT result;
if (texture->access != SDL_TEXTUREACCESS_LOCAL) {
SDL_SetError("Can't lock remote video memory");
return -1;
}
d3drect.left = rect->x; d3drect.left = rect->x;
d3drect.right = rect->x + rect->w; d3drect.right = rect->x + rect->w;
d3drect.top = rect->y; d3drect.top = rect->y;
......
...@@ -304,7 +304,7 @@ GDI_CreateTexture(SDL_Renderer * renderer, SDL_Texture * texture) ...@@ -304,7 +304,7 @@ GDI_CreateTexture(SDL_Renderer * renderer, SDL_Texture * texture)
} }
data->pitch = (texture->w * SDL_BYTESPERPIXEL(data->format)); data->pitch = (texture->w * SDL_BYTESPERPIXEL(data->format));
if (data->yuv || texture->access == SDL_TEXTUREACCESS_LOCAL if (data->yuv || texture->access == SDL_TEXTUREACCESS_STREAMING
|| texture->format != display->current_mode.format) { || texture->format != display->current_mode.format) {
int bmi_size; int bmi_size;
LPBITMAPINFO bmi; LPBITMAPINFO bmi;
......
...@@ -63,8 +63,7 @@ LoadSprite(char *file) ...@@ -63,8 +63,7 @@ LoadSprite(char *file)
/* Create textures from the image */ /* Create textures from the image */
for (i = 0; i < state->num_windows; ++i) { for (i = 0; i < state->num_windows; ++i) {
SDL_SelectRenderer(state->windows[i]); SDL_SelectRenderer(state->windows[i]);
sprites[i] = sprites[i] = SDL_CreateTextureFromSurface(0, temp);
SDL_CreateTextureFromSurface(0, SDL_TEXTUREACCESS_REMOTE, temp);
if (!sprites[i]) { if (!sprites[i]) {
fprintf(stderr, "Couldn't create texture: %s\n", SDL_GetError()); fprintf(stderr, "Couldn't create texture: %s\n", SDL_GetError());
SDL_FreeSurface(temp); SDL_FreeSurface(temp);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment