从图像文件中读取纹理信息并使用SDL2获取像素的最佳方法是什么?

时间:2014-04-13 00:41:30

标签: c++ sdl

您可以使用CreateTexture()在SDL2中创建纹理,然后使用LockTexture()访问该纹理中的像素。但为此,您需要将SDL_TEXTUREACCESS_STREAMING标志传递给CreateTexture调用。

有一个相当标准的辅助库,用于加载名为SDL_image的图像。我用它来将图像文件读入纹理(纹理是偶然观察者的图形卡驻留图像)。我目前正在使用IMG_LoadTexture()加载我的纹理。我的问题是在这种情况下我无法看到如何设置SDL_TEXTUREACCESS_STREAMING标志。所以我无法获得加载了SDL_image的纹理的像素数据?

我想要获得像素的原因是从中提取九个补丁数据。 (我最终可能会有9个纹理)。所以我在开始时只需要一次这个信息,我只需要读取纹理数据,而不是写它。如果可能的话,我也想使用预先存在的图像文件读取库。

所以问题是:从图像文件中读取纹理信息并使用SDL2获取像素的最佳方法是什么?

1 个答案:

答案 0 :(得分:0)

决定最好用曲面做,然后转换为纹理。这段代码有效。它只会在九个补丁的左上角保持简单。 (这也忽略了九个补丁图像的第一行和第九列中补丁大小调整信息的复杂性。)

typedef unsigned char byte_t;

NinePatch::NinePatch(const string fname, SDL_Renderer * renderer) {

    // get the surface and the bits per pixel
    SDL_Surface * surface = IMG_Load(fname.c_str());
    int bytes_per_pixel = surface->format->BytesPerPixel;

    // keep things simple by only looking at 4 byte/pixel nine-patches
    if (bytes_per_pixel != 4) {
        log_msg("Loading " + fname + 
                " expecting pixel data to be 4 but it has: " + 
                to_string(bytes_per_pixel));
        exit(1);
    }

    // offsets into the surface that divide the surface into a nine-patch
    unsigned int left, right, top, bottom;

    // find the widths we need by looking at the top row of pixels
    byte_t * ptr = (byte_t*)surface->pixels;
    uint32_t pixel, last_pixel = 0;
    for (int i = 0; i < surface->w; i++) {

        // we know they're 4 byte pixels cause otherwise we don't get here.
        pixel = *(uint32_t*)ptr;

        // look for "edges" in the top row of pixel data
        if (pixel > last_pixel) {
            left = i;
        }
        else if (pixel < last_pixel) {
            right = i;            
        }
        last_pixel = pixel;

        // get the next pixel across
        ptr += bytes_per_pixel;
    }


    // find the heights we need by looking at the left column of pixels
    ptr = (byte_t*)surface->pixels;
    last_pixel = 0;
    for (int i = 0; i < surface->h; i++) {

        // we know they're 4 byte pixels cause otherwise we don't get here.
        pixel = *(uint32_t*)ptr;

        // look for "edges" in the left column of pixel data
        if (pixel > last_pixel) {
            top = i;
        }
        else if (pixel < last_pixel) {
            bottom = i;
        }
        last_pixel = pixel;

        // get the next pixel down
        ptr += bytes_per_pixel * surface->w;
    }

    // SDL interprets each pixel as a 32-bit number, so our masks 
    // must depend on the endianness (byte order) of the machine 
    Uint32 rmask, gmask, bmask, amask;
#if SDL_BYTEORDER == SDL_BIG_ENDIAN
    rmask = 0xff000000;
    gmask = 0x00ff0000;
    bmask = 0x0000ff00;
    amask = 0x000000ff;
#else
    rmask = 0x000000ff;
    gmask = 0x0000ff00;
    bmask = 0x00ff0000;
    amask = 0xff000000;
#endif

    const uint32_t unused_flags = 0;
    const int pixel_size = 32; // in bits

    // scratch surface we use for breaking the nine-patch 
    // surface into little textures.
    SDL_Surface * s;
    SDL_Rect src_rect;

    // create a surface to hold the top left corner
    s = SDL_CreateRGBSurface(unused_flags, left, top, 
                             pixel_size, rmask, gmask, bmask, amask);

    // copy part of the nine-patch image surface into the new surface
    src_rect.x = 0;
    src_rect.y = 0;
    src_rect.w = left;
    src_rect.h = top;
    SDL_BlitSurface(surface, &src_rect, s, NULL);

    // convert the new corner surface into a texture
    top_left_texture =  SDL_CreateTextureFromSurface(renderer, s);

    // free the scratch surface
    SDL_FreeSurface(s);
}