diff --git a/sc2/doc/devel/gfxlib b/sc2/doc/devel/gfxlib index 7bd75f4e5..5ffef3633 100644 --- a/sc2/doc/devel/gfxlib +++ b/sc2/doc/devel/gfxlib @@ -47,7 +47,10 @@ drawable.h: defines FRAME_DESC and DRAWABLE_DESC, and the pointer an element "DataOffs", which, horrifyingly, appears to be a deliberate index past the end of the struct. Given a PFRAME_DESC x, (void *)(x[x->DataOffs]) is a void pointer - castable to TFB_Image.) + castable to TFB_Image. Preliminary investigations into + this code make a conversion of DataOffs into a void + pointer both feasible and more efficient on modern + architectures.) font.h: defines FONT_DESC and PFONT_DESC. @@ -215,10 +218,6 @@ void TFB_DrawImage_FilledImage (TFB_Image *img, ---- -There are some obvious operations missing here. Creation and -destruction of TFB_Images are the main gaps. The ability to 'wrap' a -TFB_Canvas with a fresh TFB_Image would probably be nice too. - TFB_DrawCanvas -------------- @@ -250,7 +249,78 @@ void TFB_DrawCanvas_FilledImage (TFB_Image *img, ---- -Commentary on needed operations also mirrors that of TFB_Image. +Creation and Destruction of TFB_Images, TFB_Canvases, and TFB_Palettes +---------------------------------------------------------------------- + +Various commands exist for creating and destroying the TFB_Draw data +types. The concept of "ownership" is critical here. If a data object +owns a pointer inside of it, that pointer's referent is deallocated +when the data object is deallocated. If a pointer variable owns its +referent, it's permissible to delete it. + +TFB_Canvas and TFB_Palette are primitives. TFB_Image owns NormalImg, +ScaledImg, and Palette, and will delete them when it is itself +deleted. + +That said, here are the routines: + +--- + +TFB_Image *TFB_DrawImage_New (TFB_Canvas canvas) + +Creates a new TFB_Image, which the caller then owns. The caller must +own the canvas, and transfers ownership of that canvas to the image. +The Palette value is automatically created (and the image owns it); +ScaledImg will be NULL until you scale the image and draw it to the +screen. + +--- + +void TFB_DrawImage_Delete (TFB_Image *image) + +Deletes the image, and all non-NULL components. You must own the +image you delete. + +--- + +TFB_Canvas TFB_DrawCanvas_New_TrueColor (int w, int h, BOOLEAN has_alpha); + +TFB_Canvas TFB_DrawCanvas_New_Paletted (int w, int h, + TFB_Palette *palette, + int transparent_index); + +These create new TFB_Canvases, which the caller will then own. Width +and height are straightforward. The TrueColor variant produces Canvases +with the same color depth and pixel format as the screen. The +has_alpha flag indicates whether or not the canvas has an alpha +channel. + +The Paletted variant produces 8-bit paletted canvases. The palette +argument is optional (it can be NULL, in which case you'll need to set +it later - TFB_Images tend to do this when drawn), as is the +transparent_index (if -1, there is no transparency; otherwise, it's +the index of the transparent color). + +--- + +TFB_Canvas TFB_DrawCanvas_ToScreenFormat (TFB_Canvas canvas); + +Returns a canvas that, if possible, matches the graphics configuration +of the screen. You must own the source canvas. If the conversion is +possible, it makes the conversion, deletes its argument, and returns +the converted version; if conversion is not possible, the canvas is +returned intact. + +Regardless of success or failure, the caller owns the result. + +--- + +TFB_Palette *TFB_DrawCanvas_ExtractPalette (TFB_Canvas canvas); + +Allocates and returns a 256-entry TFB_Palette array that describes the +palette of the canvas, or returns NULL if the canvas is true-color. +If the result is non-NULL, the caller owns the result. The caller +need not own canvas. DRAWCMD LIBRARY diff --git a/sc2/src/sc2code/libs/graphics/sdl/3do_blt.c b/sc2/src/sc2code/libs/graphics/sdl/3do_blt.c index 69683d1c3..e51db2d78 100644 --- a/sc2/src/sc2code/libs/graphics/sdl/3do_blt.c +++ b/sc2/src/sc2code/libs/graphics/sdl/3do_blt.c @@ -81,6 +81,7 @@ blt (PRECT pClipRect, PRIMITIVEPTR PrimPtr) SDL_Surface *new_surf; img->scale = gscale; + img->dirty = FALSE; new_surf = zoomSurface (img->NormalImg, gscale / 256.0f, gscale / 256.0f, SMOOTHING_OFF); @@ -88,16 +89,7 @@ blt (PRECT pClipRect, PRIMITIVEPTR PrimPtr) { if (!new_surf->format->palette) { - img->ScaledImg = TFB_DisplayFormatAlpha (new_surf); - if (img->ScaledImg) - { - SDL_FreeSurface(new_surf); - } - else - { - fprintf (stderr, "blt(): TFB_DisplayFormatAlpha failed\n"); - img->ScaledImg = new_surf; - } + img->ScaledImg = TFB_DrawCanvas_ToScreenFormat (new_surf); } else { diff --git a/sc2/src/sc2code/libs/graphics/sdl/3do_getbody.c b/sc2/src/sc2code/libs/graphics/sdl/3do_getbody.c index 1bf85c202..ef0b3bbf3 100644 --- a/sc2/src/sc2code/libs/graphics/sdl/3do_getbody.c +++ b/sc2/src/sc2code/libs/graphics/sdl/3do_getbody.c @@ -56,7 +56,7 @@ process_image (FRAMEPTR FramePtr, SDL_Surface *img[], AniData *ani, int cel_ct) hx = ani[cel_ct].hotspot_x; hy = ani[cel_ct].hotspot_y; - FramePtr->DataOffs = (BYTE *)TFB_LoadImage (img[cel_ct]) - (BYTE *)FramePtr; + FramePtr->DataOffs = (BYTE *)TFB_DrawImage_New (img[cel_ct]) - (BYTE *)FramePtr; tfbimg = (TFB_Image *)((BYTE *)FramePtr + FramePtr->DataOffs); tfbimg->colormap_index = ani[cel_ct].colormap_index; @@ -123,7 +123,7 @@ process_font (FRAMEPTR FramePtr, SDL_Surface *img[], int cel_ct) img[cel_ct] = new_surf; - FramePtr->DataOffs = (BYTE *)TFB_LoadImage (img[cel_ct]) - (BYTE *)FramePtr; + FramePtr->DataOffs = (BYTE *)TFB_DrawImage_New (img[cel_ct]) - (BYTE *)FramePtr; img[cel_ct] = ((TFB_Image *)((BYTE *)FramePtr + FramePtr->DataOffs))->NormalImg; SetFrameHotSpot (FramePtr, MAKE_HOT_SPOT (hx, hy)); @@ -722,17 +722,10 @@ _request_drawable (COUNT NumFrames, DRAWABLE_TYPE DrawableType, TFB_Image *Image; if (DrawableType == RAM_DRAWABLE - && (Image = TFB_LoadImage (SDL_CreateRGBSurface ( - SDL_SWSURFACE, - imgw, - imgh, - 32, - 0x00FF0000, - 0x0000FF00, - 0x000000FF, - 0x00000000 - )))) + && (Image = TFB_DrawImage_New (TFB_DrawCanvas_New_TrueColor (imgw, imgh, FALSE)))) + { FramePtr->DataOffs = (BYTE *)Image - (BYTE *)FramePtr; + } TYPE_SET (FramePtr->TypeIndexAndFlags, DrawableType); INDEX_SET (FramePtr->TypeIndexAndFlags, NumFrames); diff --git a/sc2/src/sc2code/libs/graphics/sdl/canvas.c b/sc2/src/sc2code/libs/graphics/sdl/canvas.c index fe6315929..156a51f94 100644 --- a/sc2/src/sc2code/libs/graphics/sdl/canvas.c +++ b/sc2/src/sc2code/libs/graphics/sdl/canvas.c @@ -131,3 +131,90 @@ TFB_DrawCanvas_FilledImage (TFB_Image *img, int x, int y, BOOLEAN scaled, int r, SDL_BlitSurface(surf, NULL, (NativeCanvas) target, &targetRect); UnlockMutex (img->mutex); } + +TFB_Canvas TFB_DrawCanvas_New_TrueColor (int w, int h, BOOLEAN hasalpha) +{ + SDL_Surface *new_surf; + new_surf = SDL_CreateRGBSurface (SDL_SWSURFACE, w, h, 32, 0x000000ff, 0x0000ff00, 0x00ff0000, hasalpha ? 0xff000000 : 0); + if (!new_surf) { + fprintf(stderr, "INTERNAL PANIC: Failed to create TFB_Canvas: %s", SDL_GetError()); + exit(-1); + } + return new_surf; +} + +TFB_Canvas +TFB_DrawCanvas_New_Paletted (int w, int h, TFB_Palette *palette, int transparent_index) +{ + SDL_Surface *new_surf; + new_surf = SDL_CreateRGBSurface (SDL_SWSURFACE, w, h, 8, 0, 0, 0, 0); + if (!new_surf) { + fprintf(stderr, "INTERNAL PANIC: Failed to create TFB_Canvas: %s\n", SDL_GetError()); + exit(-1); + } + if (palette != NULL) + { + SDL_SetColors(new_surf, (SDL_Color *)palette, 0, 256); + } + if (transparent_index >= 0) + { + SDL_SetColorKey (new_surf, SDL_SRCCOLORKEY, transparent_index); + } + else + { + SDL_SetColorKey (new_surf, 0, 0); + } + return new_surf; +} + +void +TFB_DrawCanvas_Delete (TFB_Canvas canvas) +{ + if (!canvas) + { + fprintf(stderr, "INTERNAL PANIC: Attempted to delete a NULL canvas!\n"); + /* Should we actually die here? */ + } + else + { + SDL_FreeSurface ((SDL_Surface *) canvas); + } + +} + +TFB_Palette * +TFB_DrawCanvas_ExtractPalette (TFB_Canvas canvas) +{ + int i; + TFB_Palette *result = (TFB_Palette*) HMalloc (sizeof (TFB_Palette) * 256); + SDL_Surface *surf = (SDL_Surface *)canvas; + + if (!surf->format->palette) + { + return NULL; + } + + for (i = 0; i < 256; ++i) + { + result[i].r = surf->format->palette->colors[i].r; + result[i].g = surf->format->palette->colors[i].g; + result[i].b = surf->format->palette->colors[i].b; + } + return result; +} + +TFB_Canvas +TFB_DrawCanvas_ToScreenFormat (TFB_Canvas canvas) +{ + SDL_Surface *result = TFB_DisplayFormatAlpha (canvas); + if (result == NULL) + { + fprintf (stderr, "WARNING: Could not convert sprite-canvas to display format. Expect performance penalties.\n"); + return canvas; + } + else + { + TFB_DrawCanvas_Delete(canvas); + return result; + } +} diff --git a/sc2/src/sc2code/libs/graphics/sdl/sdl_common.c b/sc2/src/sc2code/libs/graphics/sdl/sdl_common.c index 0c7a32ab1..2446836d0 100644 --- a/sc2/src/sc2code/libs/graphics/sdl/sdl_common.c +++ b/sc2/src/sc2code/libs/graphics/sdl/sdl_common.c @@ -125,46 +125,6 @@ TFB_ProcessEvents () } } -TFB_Image* -TFB_LoadImage (SDL_Surface *img) -{ - TFB_Image *myImage; - - myImage = (TFB_Image*) HMalloc (sizeof (TFB_Image)); - myImage->mutex = CreateMutex (); - myImage->ScaledImg = NULL; - myImage->colormap_index = -1; - - if (img->format->palette) - { - int i; - myImage->Palette = (TFB_Palette*) HMalloc (sizeof (TFB_Palette) * 256); - for (i = 0; i < 256; ++i) - { - myImage->Palette[i].r = img->format->palette->colors[i].r; - myImage->Palette[i].g = img->format->palette->colors[i].g; - myImage->Palette[i].b = img->format->palette->colors[i].b; - } - myImage->NormalImg = img; - } - else - { - myImage->Palette = NULL; - myImage->NormalImg = TFB_DisplayFormatAlpha (img); - if (myImage->NormalImg) - { - SDL_FreeSurface (img); - } - else - { - fprintf (stderr, "TFB_LoadImage(): TFB_DisplayFormatAlpha failed\n"); - myImage->NormalImg = img; - } - } - - return(myImage); -} - void TFB_SwapBuffers () { @@ -178,6 +138,7 @@ TFB_SwapBuffers () #endif } +/* Probably ought to clean this away at some point. */ SDL_Surface* TFB_DisplayFormatAlpha (SDL_Surface *surface) { @@ -682,27 +643,7 @@ TFB_FlushGraphics () // Only call from main thread!! case TFB_DRAWCOMMANDTYPE_DELETEIMAGE: { TFB_Image *DC_image = (TFB_Image *)DC.data.deleteimage.image; - - if (DC_image == 0) - { - fprintf (stderr, "DCQ ERROR: DELETEIMAGE passed null image ptr\n"); - break; - } - LockMutex (DC_image->mutex); - - SDL_FreeSurface (DC_image->NormalImg); - - if (DC_image->ScaledImg) { - SDL_FreeSurface (DC_image->ScaledImg); - } - - if (DC_image->Palette) - HFree (DC_image->Palette); - - UnlockMutex (DC_image->mutex); - DestroyMutex (DC_image->mutex); - - HFree (DC_image); + TFB_DrawImage_Delete(DC_image); break; } case TFB_DRAWCOMMANDTYPE_SENDSIGNAL: diff --git a/sc2/src/sc2code/libs/graphics/sdl/sdl_common.h b/sc2/src/sc2code/libs/graphics/sdl/sdl_common.h index 0c6034d9c..206eb9df5 100644 --- a/sc2/src/sc2code/libs/graphics/sdl/sdl_common.h +++ b/sc2/src/sc2code/libs/graphics/sdl/sdl_common.h @@ -46,9 +46,6 @@ extern int GfxFlags; void ScreenOrigin (FRAME Display, COORD sx, COORD sy); void LoadDisplay (PDISPLAY_INTERFACE *pDisplay); -TFB_Image *TFB_LoadImage (SDL_Surface *img); -void TFB_FreeImage (TFB_Image *img); - void TFB_SwapBuffers (); SDL_Surface* TFB_DisplayFormatAlpha (SDL_Surface *surface); void TFB_BlitSurface (SDL_Surface *src, SDL_Rect *srcrect, SDL_Surface *dst, diff --git a/sc2/src/sc2code/libs/graphics/tfb_draw.c b/sc2/src/sc2code/libs/graphics/tfb_draw.c index ca8a81c81..0091b6d9d 100644 --- a/sc2/src/sc2code/libs/graphics/tfb_draw.c +++ b/sc2/src/sc2code/libs/graphics/tfb_draw.c @@ -242,3 +242,51 @@ TFB_DrawImage_FilledImage (TFB_Image *img, int x, int y, BOOLEAN scaled, int r, target->dirty = TRUE; UnlockMutex (target->mutex); } + +TFB_Image * +TFB_DrawImage_New (TFB_Canvas canvas) +{ + TFB_Image *img = HMalloc (sizeof (TFB_Image)); + img->mutex = CreateMutex (); + img->ScaledImg = NULL; + img->colormap_index = -1; + + img->Palette = TFB_DrawCanvas_ExtractPalette (canvas); + + if (img->Palette) + { + img->NormalImg = canvas; + } + else + { + img->NormalImg = TFB_DrawCanvas_ToScreenFormat (canvas); + } + + return img; +} + +void +TFB_DrawImage_Delete (TFB_Image *image) +{ + if (image == 0) + { + fprintf (stderr, "INTERNAL ERROR: Tried to delete a null image!\n"); + /* Should we die here? */ + return; + } + LockMutex (image->mutex); + + TFB_DrawCanvas_Delete (image->NormalImg); + + if (image->ScaledImg) { + TFB_DrawCanvas_Delete (image->ScaledImg); + } + + if (image->Palette) + HFree (image->Palette); + + UnlockMutex (image->mutex); + DestroyMutex (image->mutex); + + HFree (image); +} diff --git a/sc2/src/sc2code/libs/graphics/tfb_draw.h b/sc2/src/sc2code/libs/graphics/tfb_draw.h index bb98c23c5..b08c29879 100644 --- a/sc2/src/sc2code/libs/graphics/tfb_draw.h +++ b/sc2/src/sc2code/libs/graphics/tfb_draw.h @@ -64,14 +64,23 @@ void TFB_DrawScreen_WaitForSignal (void); void TFB_DrawScreen_SetPalette (int paletteIndex, int r, int g, int b); void TFB_FlushPaletteCache (void); +TFB_Image *TFB_DrawImage_New (TFB_Canvas canvas); +void TFB_DrawImage_Delete (TFB_Image *image); + void TFB_DrawImage_Line (int x1, int y1, int x2, int y2, int r, int g, int b, TFB_Image *dest); void TFB_DrawImage_Rect (PRECT rect, int r, int g, int b, TFB_Image *image); void TFB_DrawImage_Image (TFB_Image *img, int x, int y, BOOLEAN scaled, TFB_Palette *palette, TFB_Image *target); void TFB_DrawImage_FilledImage (TFB_Image *img, int x, int y, BOOLEAN scaled, int r, int g, int b, TFB_Image *target); +TFB_Canvas TFB_DrawCanvas_New_TrueColor (int w, int h, BOOLEAN hasalpha); +TFB_Canvas TFB_DrawCanvas_New_Paletted (int w, int h, TFB_Palette *palette, int transparent_index); +TFB_Canvas TFB_DrawCanvas_ToScreenFormat (TFB_Canvas canvas); +void TFB_DrawCanvas_Delete (TFB_Canvas canvas); + void TFB_DrawCanvas_Line (int x1, int y1, int x2, int y2, int r, int g, int b, TFB_Canvas dest); void TFB_DrawCanvas_Rect (PRECT rect, int r, int g, int b, TFB_Canvas image); void TFB_DrawCanvas_Image (TFB_Image *img, int x, int y, BOOLEAN scaled, TFB_Palette *palette, TFB_Canvas target); void TFB_DrawCanvas_FilledImage (TFB_Image *img, int x, int y, BOOLEAN scaled, int r, int g, int b, TFB_Canvas target); +TFB_Palette *TFB_DrawCanvas_ExtractPalette (TFB_Canvas canvas); #endif