基于最近邻算法的Bitmap缩放结果异常问题排查
C语言最近邻图像缩放右侧异常问题修复
我在开发瓦片游戏时用C语言实现最近邻图像缩放,将64×64的源图缩放到30×30这类非2的幂尺寸时,缩放后的图像右侧显示异常。当前实现代码如下:
u32 *DestPixel = (u32 *)DestBitmap.Pixels; u32 *SourcePixel = (u32 *)SourceBitmap->Pixels; f64 RatioX = (f64)SourceBitmap->Width / (f64)DestBitmap.Width; f64 RatioY = (f64)SourceBitmap->Height / (f64)DestBitmap.Height; for(s32 Y = 0; Y < DestBitmap.Height; ++Y) { for(s32 X = 0; X < DestBitmap.Width; ++X) { s32 ScaledX = (s32)(X * RatioX); s32 ScaledY = (s32)(Y * RatioY); s32 DestOffset = Y*DestBitmap.Width + X; s32 SourceOffset = ScaledY*SourceBitmap->Width + ScaledX; *(DestPixel + DestOffset) = *(SourcePixel + SourceOffset); } }
试过对ScaledX/ScaledY做四舍五入或向下取整,问题依旧。
问题原因
原代码的映射逻辑是直接用目标像素索引乘以比例,这种方式会导致目标图像的最后几个像素无法映射到源图像的最右侧/最底部像素。比如64→30的缩放,比例≈2.1333,目标X=29时,29×2.1333≈61.866,截断后得到61,而源图像的最大X索引是63,这就导致源图像的62、63列像素没有被映射到目标图像中,最终右侧显示异常。
正确的最近邻映射应该保证目标图像的首尾像素对应源图像的首尾像素,需要基于坐标范围来计算,而不是简单的索引乘比例。
修复代码
u32 *DestPixel = (u32 *)DestBitmap.Pixels; u32 *SourcePixel = (u32 *)SourceBitmap->Pixels; // 改用范围映射:将目标坐标的[0, DestW-1]映射到源坐标的[0, SourceW-1] for(s32 Y = 0; Y < DestBitmap.Height; ++Y) { for(s32 X = 0; X < DestBitmap.Width; ++X) { // 计算目标像素对应的源像素索引,确保首尾对齐 s32 ScaledX = (s32)( (X / (f64)(DestBitmap.Width - 1)) * (SourceBitmap->Width - 1) ); s32 ScaledY = (s32)( (Y / (f64)(DestBitmap.Height - 1)) * (SourceBitmap->Height - 1) ); // 确保索引不越界(极端情况浮点精度问题可能导致ScaledX超过SourceW-1) ScaledX = clamp(ScaledX, 0, SourceBitmap->Width - 1); ScaledY = clamp(ScaledY, 0, SourceBitmap->Height - 1); s32 DestOffset = Y * DestBitmap.Width + X; s32 SourceOffset = ScaledY * SourceBitmap->Width + ScaledX; DestPixel[DestOffset] = SourcePixel[SourceOffset]; } } // 自定义clamp函数处理边界 s32 clamp(s32 val, s32 min, s32 max) { if(val < min) return min; if(val > max) return max; return val; }
修复逻辑说明
- 用
X/(DestW-1)将目标X坐标归一化到[0,1]区间,再乘以SourceW-1映射到源图像的[0, SourceW-1]范围,这样目标的最后一个像素(X=DestW-1)会精确对应到源的最后一个像素(X=SourceW-1)。 - 增加
clamp函数处理浮点精度可能带来的索引越界问题,避免访问非法内存。
内容的提问来源于stack exchange,提问作者user13606245
相关产品推荐
相关产品推荐

