【问题标题】:Rotating SDL_Surface unpredictably produces incorrect result不可预测地旋转 SDL_Surface 会产生不正确的结果
【发布时间】:2020-09-06 22:48:47
【问题描述】:

一直在用 C# 中的 SDL2 进行试验,并尝试创建一个 maxrects 驱动的纹理打包器。为了以节省空间的方式打包图集,几乎总是需要旋转纹理。该程序使用 SDL_Surfaces 从文件中加载图像并将它们 blit 到更大的 SDL_Surface 并将其写入磁盘。这就是我实现轮换的方式:

// get pointer to original SDL_Surface
var sptr = (SDL_Surface*)surfaces[bestIndex].Item2;
// check if maxrects algorithm has rotated the rectangle "best"
if (best.w != sptr->w) {
    var rotd = (SDL_Surface*)SDL_CreateRGBSurfaceWithFormat(0, sptr->h, sptr->w, 32, SDL_PIXELFORMAT_RGBA8888);
    IntPtr rotdPixels = rotd->pixels;
    IntPtr sptrPixels = sptr->pixels;

    for (int y = 0; y < sptr->h; y++) {
        for (int x = 0; x < sptr->w; x++) {
            // get target pointer by swapping dimensions and flipping one, resulting in a rotation
            uint* target = (uint*)(rotdPixels + x * rotd->pitch + (sptr->h - y - 1) * 4);
            // set target from the expected location. Bswsp is necessary.
            *target = System.Buffers.Binary.BinaryPrimitives.ReverseEndianness(*(uint*)(sptrPixels + y * sptr->pitch + x * 4));
        }
    }

    if (SDL_BlitSurface(new IntPtr(rotd), IntPtr.Zero, atlas, ref best) < 0)
        throw new Exception(SDL_GetError());
} else {
    if (SDL_BlitSurface(new IntPtr(sptr), IntPtr.Zero, atlas, ref best) < 0)
        throw new Exception(SDL_GetError());
}

这是一种非常不安全的方法,我很惊讶它在当前形式下的效果和它一样好,但是它在生成的图集中出现了一个看似不可预测的错误(如果我在 blitting 之前保存旋转的表面,则在单​​独的图像中) ,这只能通过错误转换的图像本身来最好地描述:

Imgur 不保留原始数据,而是在此处 Google Drive 链接到之前和之后:
After rotation (png)

Before rotation (jpg)

请注意,这不仅限于 .jpg 文件,该格式也不一定会导致问题,.png 文件偶尔也会这样做,但频率较低。如果我要解释发生了什么:图像被轻微压扁,一个扇区被切割并重新定位,颜色分裂成几乎像 CRT 一样的扫描线效果,似乎以 3 行的模式重复,但仍然在一定程度上反映了原始图片,旋转。

有谁知道这可能是什么原因造成的,如何防止它,或者如何以编程方式检测它何时会发生并避免它。或者,如果必须是这样的话,一个更好的方法来做到这一点?我目前无法找到导致此问题的格式/尺寸的任何一致性,并且已确认这不是导致问题的 blitting 过程,并且 maxrects 算法按预期工作 - 返回的矩形是有效的。如果您能提供帮助,请提前致谢..

【问题讨论】:

  • 您是否尝试过使用图像的原始格式而不是 RGBA8888?
  • 看起来您的颜色通道顺序错误。什么是源表面格式?你确定你需要反向字节序吗?
  • @keltar 所有表面都以 RGBA8888 格式加载,程序中不使用其他格式。反转字节顺序时,不仅正确旋转的图像看起来很完美,而且旋转错误的图像看起来也更准确。
  • @Aram 所有表面都以 RGBA8888 格式加载,并且所有表面都以原始形式正确表示,它们正确保存。除非 SDL 库以某种方式忽略格式转换并给出不正确的大小像素,否则它会在将图像写回磁盘时自行恢复(这是可能的,但似乎不太可能),我认为没有必要为不同的格式创建不同的例程。这也导致:我如何检测这种格式变化?
  • @SFBDragon 用 alpha 交换红色将产生半透明。您的图像看起来非常半透明。检查surface-&gt;format-&gt;format的实际值。

标签: c# sdl-2 image-rotation


【解决方案1】:

正如@kelter 指出的那样,我最初应该检查表面格式。如果需要在图像加载时选择性地转换表面,代码可以按预期工作。

作为对具体奇怪之处的解释,我使用的两种文件格式恰好是 ABGR8888 和 RGB24,这就是为什么 byteswap 部分完成了原始工作,3 的模式是由于 3byte 与 4byte 内存布局,通过“跳过”某些像素导致挤压,溢出导致最终图像中出现额外的重复剪切。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-04-04
    • 1970-01-01
    • 2020-12-14
    • 2015-10-12
    • 2015-01-06
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多