C# 不可预测地旋转SDL_曲面会产生错误的结果

C# 不可预测地旋转SDL_曲面会产生错误的结果,c#,sdl-2,image-rotation,C#,Sdl 2,Image Rotation,已经在c#中试用SDL2,并尝试创建maxrects驱动的纹理打包器。为了以节省空间的方式打包地图集,旋转纹理几乎总是必要的。该程序使用SDL_曲面从文件中加载图像,并将其blit到更大的SDL_曲面中,然后将其写入磁盘。这就是我实现旋转的方式: // get pointer to original SDL_Surface var sptr = (SDL_Surface*)surfaces[bestIndex].Item2; // check if maxrects algorithm has

已经在c#中试用SDL2,并尝试创建maxrects驱动的纹理打包器。为了以节省空间的方式打包地图集,旋转纹理几乎总是必要的。该程序使用SDL_曲面从文件中加载图像,并将其blit到更大的SDL_曲面中,然后将其写入磁盘。这就是我实现旋转的方式:

// 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());
}
//获取指向原始SDL_曲面的指针
var sptr=(SDL_表面*)表面[bestIndex];
//检查maxrects算法是否将矩形旋转为“最佳”
if(best.w!=sptr->w){
var rotd=(SDL_曲面*)SDL_创建RGBSurfaceWithFormat(0,sptr->h,sptr->w,32,SDL_像素格式RGBA8888);
IntPtr rotdPixels=rotd->pixels;
IntPtr sptrPixels=sptr->pixels;
对于(int y=0;yh;y++){
对于(intx=0;xw;x++){
//通过交换尺寸并翻转一个尺寸来获取目标指针,从而产生旋转
uint*目标=(uint*)(旋转像素+x*旋转->俯仰+(sptr->h-y-1)*4);
//从预期位置设置目标。需要Bswsp。
*target=System.Buffers.Binary.BinaryPrimitives.ReverseEndianness(*(uint*)(sptrPixels+y*sptr->pitch+x*4));
}
}
if(SDL_BlitSurface(新IntPtr(rotd)、IntPtr.0、阿特拉斯、参考最佳)小于0)
抛出新异常(SDL_GetError());
}否则{
if(SDL_BlitSurface(新IntPtr(sptr)、IntPtr.Zero、atlas、最佳参考)小于0)
抛出新异常(SDL_GetError());
}
这是一种非常不安全的方法,我很惊讶它的工作原理和它当前的形式一样好,但是它在生成的图集中(如果我在光点显示之前保存旋转的曲面,则在单独的图像中)出现了一个似乎不可预测的错误,只能通过错误转换的图像本身来最好地描述:

Imgur不保留原始数据,而是在这里谷歌驱动器链接到之前和之后:

请注意,这不仅限于.jpg文件,这种格式也不一定会导致问题,.png文件偶尔也会这样做,但不太常见。如果我要解释发生了什么:图像被轻微挤压,一个扇形切割和重新定位,颜色分裂成几乎像CRT一样的扫描线效果,看起来以3行的模式重复,但仍然多少反映了原始图像,旋转


有没有人知道是什么原因导致了这种情况,如何防止它,或者如何通过编程检测何时会发生并避免它。或者,如果必须这样做,还有更好的方法吗?我目前找不到导致此问题的格式/维度的任何一致性,并已确认导致此问题的不是blitting过程,并且maxrects算法按预期工作-返回的矩形有效。如果您能提供帮助,请提前感谢。

正如@kelter所指出的,我应该先检查曲面格式。如果需要加载图像,可以选择转换曲面,代码可以按预期工作


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

您是否尝试使用图像的原始格式而不是RGBA888?看起来您的颜色通道顺序错误。什么是源曲面格式?您确定需要反向endianess吗?@keltar所有曲面均以RGBA8888格式加载,程序中不使用其他格式。反转endianness时,不仅正确旋转的图像看起来很完美,而且错误旋转的图像看起来颜色更精确。@Aram所有曲面都以RGBA8888格式加载,并且所有曲面都以原始形式正确表示,它们保存正确。除非SDL库忽略了格式转换,并给出了错误的大小像素,当它将图像写回磁盘时(这是可能的,但似乎不太可能),否则我认为没有必要为不同的格式创建不同的例程。这也引出了一个问题:如何检测这种格式的变化?@SFBDragon用alpha替换红色将产生半透明。你的图像看起来非常半透明。检查
surface->format->format
的实际值。