Day07_Sprite是如何绘制到界面上的

返回目录

前面的章节,我们讲述了Sprite加载的原理,最终是放到一个 SpriteCache这样一个数据结构里面。
今天,我们继续看一下在loop里面,程序如何将这些Sprite绘制到界面上。
首先 研究 SpriteCache.cpp ,在《Day05_OpenTTD的Sprite介绍》 我们了解到 游戏启动阶段,LoadNextSprite() 函数加载 SpriteCache的过程。

在 spritecache.h 里面还定义了 GetRawSprite(),这个函数向外提供服务,通过SpriteID获取Sprite数据
GetSprite() 返回的 Sprite数据结构如下():

/** Data structure describing a sprite. */
struct Sprite {
    uint16 height; ///< Height of the sprite.
    uint16 width;  ///< Width of the sprite.
    int16 x_offs;  ///< Number of pixels to shift the sprite to the right.
    int16 y_offs;  ///< Number of pixels to shift the sprite downwards.
    byte data[];   ///< Sprite data.
};

spritecache.h 通过 GetSprite()封装GetRawSprite()向外提供服务

通过 gfx.cpp DrawSpriteViewport(SpriteID img, PaletteID pal, int x, int y, const SubSprite *sub) 调用 GetSprite() 获取 Sprite后,绘制到界面上,调用的顺序如下:
gfx.cpp DrawSpriteViewport()
-> GfxMainBlitterViewport()
-> GfxBlitter<ZOOM_LVL_BASE, false>() <- 构造出 BlitterParams 供下游消费
-> BlitterFactory::GetCurrentBlitter()->Draw() <- 这里有各种类型的blitter对上面的 params进行加工

通过增加DEBUG分析,我们知道默认的加载器是 32bppOptimized.cpp
Blitter_32bppOptimized::Draw(const Blitter::BlitterParams *bp, ZoomLevel zoom)
这个Draw() 主要逻辑是 循环处理 bp->height ,也就是根据像素高一行一行处理,生成 bp->dst

From: base.hpp
    /** Parameters related to blitting. */
    struct BlitterParams {
        const void *sprite; ///< Pointer to the sprite how ever the encoder stored it
        const byte *remap;  ///< XXX -- Temporary storage for remap array

        int skip_left;      ///< How much pixels of the source to skip on the left (based on zoom of dst)
        int skip_top;       ///< How much pixels of the source to skip on the top (based on zoom of dst)
        int width;          ///< The width in pixels that needs to be drawn to dst
        int height;         ///< The height in pixels that needs to be drawn to dst
        int sprite_width;   ///< Real width of the sprite
        int sprite_height;  ///< Real height of the sprite
        int left;           ///< The left offset in the 'dst' in pixels to start drawing
        int top;            ///< The top offset in the 'dst' in pixels to start drawing

        void *dst;          ///< Destination buffer
        int pitch;          ///< The pitch of the destination buffer
    };

我们根据 dst的关键字,我们定位到 dst的赋值位置 gfx.cpp GfxBlitter()

gfx.cpp Line 1054
static void GfxBlitter(const Sprite * const sprite, int x, int y, BlitterMode mode, const SubSprite * const sub, SpriteID sprite_id, ZoomLevel zoom)
{
    const DrawPixelInfo *dpi = _cur_dpi;
...
    bp.dst = dpi->dst_ptr;
...

gfx_type.h 定义了 DrawPixelInfo 数据结构如下:

/** Data about how and where to blit pixels. */
struct DrawPixelInfo {
    void *dst_ptr;
    int left, top, width, height;
    int pitch;
    ZoomLevel zoom;
};

继续搜索关键字 _cur_dpi,与它相关联的语句大概有下面两种
_cur_dpi = &_screen; <- Draw mouse cursor on screen.
或者
if (!FillDrawPixelInfo(&tmp_dpi, left, y, max_width, ScaleGUITrad(14))) return;
old_dpi = _cur_dpi;
_cur_dpi = &tmp_dpi;

FillDrawPixelInfo() * Set up a clipping area for only drawing into a certain area.
这个函数的作用是只绘制一部分区域,这个函数并没有对 dst_ptr的定义,所以,我们看回 _screen 看看 dst_ptr是怎么来的。

sdl2_v.cpp  Line800
bool VideoDriver_SDL_Base::LockVideoBuffer()
{
...
  _screen.dst_ptr = this->GetVideoPointer();
...
}

继续 GetVideoPointer();

sdl2_default_v.cpp Line38
...
   static SDL_Surface *_sdl_surface;
...
void *VideoDriver_SDL_Default::GetVideoPointer()
{
    return _sdl_surface->pixels;
}

到这里就真相大白了,SDL_Surface是 Linux的 SDL 开放出来的数据结构:

/**
* \brief A collection of pixels used in software blitting.
*
* \note  This structure should be treated as read-only, except for \c pixels,
*        which, if not NULL, contains the raw pixel data for the surface.
*/
typedef struct SDL_Surface
{
    Uint32 flags;               /**< Read-only */
    SDL_PixelFormat *format;    /**< Read-only */
    int w, h;                   /**< Read-only */
    int pitch;                  /**< Read-only */
    void *pixels;               /**< Read-write */

    /** Application data associated with the surface */
    void *userdata;             /**< Read-write */

    /** information needed for surfaces requiring locks */
    int locked;                 /**< Read-only */
    void *lock_data;            /**< Read-only */

    /** clipping information */
    SDL_Rect clip_rect;         /**< Read-only */

    /** info for fast blit mapping to other surfaces */
    struct SDL_BlitMap *map;    /**< Private */

    /** Reference count -- used when freeing surface */
    int refcount;               /**< Read-mostly */
} SDL_Surface;

总结一下绘制的过程,SDL通过 SDL_Surface的pixels 接收 Sprite 信息,通过 Blitter_32bppOptimized的Draw()方法 更新 字段信息,最后 绘制前,通过sdl2_v.cpp LockVideoBuffer() 设置 屏幕指针,然后通过 sdl2_default_v.cpp Paint() 把sprite绘制到界面上。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值