You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

iOS及部分Android设备图片裁剪位置异常问题求助

Unity图片裁剪协程跨平台位置错误排查求助

问题代码

private IEnumerator CropImage(Texture original, int width, int height)
{
    yield return new WaitForEndOfFrame();
    float originalWidth = original.width;
    float originalHeight = original.height;
    float originalAspectRatio = originalWidth / originalHeight;
    float targetAspectRatio = (float)width / (float)height;
    float scaleFactor;
    if (originalAspectRatio >= targetAspectRatio)
    {
        // Image is wider than frame. Use height to determine scale.
        scaleFactor = height / originalHeight;
        Debug.Log("[Image Editor]: Image is wider than frame. Scale Factor: " + scaleFactor);
    }
    else
    {
        // image is taller than frame. Use width to determine scale
        scaleFactor = width / originalWidth;
        Debug.Log("[Image Editor]: Image is taller than frame. Scale Factor: " + scaleFactor);
    }

    int newWidth = Mathf.FloorToInt(originalWidth * scaleFactor * _zoomScale);
    int newHeight = Mathf.FloorToInt(originalHeight * scaleFactor * _zoomScale);

    RenderTexture rTex = new RenderTexture(newWidth, newHeight, 24, RenderTextureFormat.ARGB32);
    Graphics.Blit(original, rTex);

    Texture2D newTex = new Texture2D(width, height, TextureFormat.RGB24, false);
    RenderTexture.active = rTex;

    Vector2 imagePositions = m_Image.rectTransform.localPosition;
    float xPos = (newWidth - width) * 0.5f - imagePositions.x;
    float yPos = (newHeight - height) * 0.5f + imagePositions.y;

    newTex.ReadPixels(new Rect(new Vector2(xPos, yPos), m_Image.rectTransform.rect.size), 0, 0);
    //newTex.ReadPixels(new Rect(xPos, yPos, ImageContainer.rect.width, ImageContainer.rect.height), 0, 0);
    yield return new WaitForEndOfFrame();

    newTex.Apply();
    finalTex = newTex;
    m_CroppedImage.texture = finalTex;
    _data.OnSetImage?.Invoke(finalTex);
    UIManager.Inst.RemoveTopModal();
}

问题描述

上述代码在Unity编辑器及多数Android设备上运行正常,但在iOS和部分Android设备中出现裁剪位置偏移错误。已知RenderTexture曾存在相关平台兼容性Bug,但无法定位具体触发原因,调整ReadPixels参数后问题仍未解决,寻求技术修复方案。

可能原因及修复方案

1. RenderTexture平台兼容性差异

不同平台(尤其是iOS的Metal后端)对RenderTexture的像素对齐、色彩空间处理存在差异:

  • 创建RenderTexture时显式禁用抗锯齿,避免自动AA导致像素偏移:
    RenderTexture rTex = new RenderTexture(newWidth, newHeight, 24, RenderTextureFormat.ARGB32)
    {
        antiAliasing = 0
    };
    
  • 根据项目色彩空间设置sRGB属性,确保色彩空间一致性:
    rTex.sRGB = QualitySettings.activeColorSpace == ColorSpace.Linear;
    

2. ReadPixels坐标空间翻转问题

Metal平台纹理原点为左下角,而OpenGL为左上角,导致Y轴坐标偏移:

  • 调整Y轴坐标计算逻辑,转换为纹理空间的底部坐标:
    float yPos = (newHeight - height) * 0.5f - imagePositions.y;
    float textureY = newHeight - (yPos + height);
    newTex.ReadPixels(new Rect(xPos, textureY, width, height), 0, 0);
    

3. UI坐标转像素坐标的缩放误差

rectTransform.localPosition是UI单位,未考虑画布缩放因子:

  • 引入画布缩放因子转换坐标:
    float canvasScale = m_Image.canvas.scaleFactor;
    float xPos = (newWidth - width) * 0.5f - imagePositions.x * canvasScale;
    float yPos = (newHeight - height) * 0.5f - imagePositions.y * canvasScale;
    

4. 渲染时机同步问题

两次WaitForEndOfFrame可能导致RenderTexture未完全渲染完成就读取像素:

  • 移除第二次WaitForEndOfFrame,添加GL.Flush()确保渲染命令执行完毕:
    RenderTexture.active = rTex;
    GL.Flush();
    // 执行ReadPixels操作
    

修改后的核心代码示例

private IEnumerator CropImage(Texture original, int width, int height)
{
    yield return new WaitForEndOfFrame();
    float originalWidth = original.width;
    float originalHeight = original.height;
    float originalAspectRatio = originalWidth / originalHeight;
    float targetAspectRatio = (float)width / (float)height;
    float scaleFactor;
    if (originalAspectRatio >= targetAspectRatio)
    {
        scaleFactor = height / originalHeight;
        Debug.Log("[Image Editor]: Image is wider than frame. Scale Factor: " + scaleFactor);
    }
    else
    {
        scaleFactor = width / originalWidth;
        Debug.Log("[Image Editor]: Image is taller than frame. Scale Factor: " + scaleFactor);
    }

    float canvasScale = m_Image.canvas.scaleFactor;
    int newWidth = Mathf.FloorToInt(originalWidth * scaleFactor * _zoomScale);
    int newHeight = Mathf.FloorToInt(originalHeight * scaleFactor * _zoomScale);

    RenderTexture rTex = new RenderTexture(newWidth, newHeight, 24, RenderTextureFormat.ARGB32)
    {
        antiAliasing = 0,
        sRGB = QualitySettings.activeColorSpace == ColorSpace.Linear
    };
    Graphics.Blit(original, rTex);

    Texture2D newTex = new Texture2D(width, height, TextureFormat.RGB24, false);
    RenderTexture.active = rTex;
    GL.Flush();

    Vector2 imagePositions = m_Image.rectTransform.localPosition;
    float xPos = (newWidth - width) * 0.5f - imagePositions.x * canvasScale;
    float yPos = (newHeight - height) * 0.5f - imagePositions.y * canvasScale;
    float textureY = newHeight - (yPos + height);

    newTex.ReadPixels(new Rect(xPos, textureY, width, height), 0, 0);
    newTex.Apply();

    finalTex = newTex;
    m_CroppedImage.texture = finalTex;
    _data.OnSetImage?.Invoke(finalTex);
    UIManager.Inst.RemoveTopModal();

    // 清理资源
    RenderTexture.active = null;
    Destroy(rTex);
}

内容的提问来源于stack exchange,提问作者Eddie Piña

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.16 09:14:53