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

Android像素编辑器极端宽高比下Bitmap像素矩形化问题

问题:极端宽高比Bitmap绘制时像素矩形化

我正在开发一款Android像素艺术编辑器,发现创建宽高比极端的Bitmap(例如3x300、5x90或反向比例)时,像素会呈现轻微矩形。但宽高比相近的Bitmap(如50x40、90x80)不会出现此问题。

相关代码

边界Rect计算代码

private fun setBoundingRect() {
    val ratio = if (bitmapWidth > bitmapHeight) {
        bitmapHeight.toDouble() / bitmapWidth.toDouble()
    } else {
        bitmapWidth.toDouble() / bitmapHeight.toDouble()
    }

    val rectW: Int = if (bitmapWidth > bitmapHeight) {
        width
    } else if (bitmapHeight > bitmapWidth) {
        (height * ratio).toInt()
    } else {
        width
    }

    val rectH: Int = if (bitmapWidth > bitmapHeight)  {
        (width * ratio).toInt()
    } else if (bitmapHeight > bitmapWidth) {
        height
    } else {
        width
    }

    val canvasCenter = Point(width / 2, height / 2)

    val left = canvasCenter.x - rectW / 2
    val top = canvasCenter.y - rectH / 2
    val right = canvasCenter.x + rectW / 2
    val bottom = canvasCenter.y + rectH / 2

    boundingRect = Rect(left, top, right, bottom)
}

逻辑说明:通过计算宽高比作为缩放因子,对容器尺寸进行缩放以匹配Bitmap比例,多数情况有效,但极端比例时出问题。

onDraw方法代码

override fun onDraw(canvas: Canvas) {
    if (::drawingViewBitmap.isInitialized) {
        canvas.drawRect(boundingRect, PaintData.rectPaint)
        canvas.drawBitmap(drawingViewBitmap, null, boundingRect, null)
        drawGrid(canvas)
    }
}

问题分析与解决方案

问题核心在于整数取整导致的比例偏差:计算rectW和rectH时直接用toInt()截断小数,极端宽高比下,这种截断会让boundingRect的宽高比与原始Bitmap产生细微差异,最终导致像素拉伸变形。另外,默认的Bitmap缩放滤镜也会加剧这个问题。

修复步骤:

  1. 修正宽高比计算,消除比例偏差
    不要单独计算宽和高,应基于容器尺寸和Bitmap原始比例算出统一缩放因子,再同步计算宽高,确保boundingRect比例与Bitmap完全一致:

    private fun setBoundingRect() {
        val bitmapRatio = bitmapWidth.toDouble() / bitmapHeight.toDouble()
        val containerRatio = width.toDouble() / height.toDouble()
    
        val scaleFactor: Double = if (bitmapRatio > containerRatio) {
            // Bitmap更宽,以容器宽度为基准缩放
            width.toDouble() / bitmapWidth.toDouble()
        } else {
            // Bitmap更高,以容器高度为基准缩放
            height.toDouble() / bitmapHeight.toDouble()
        }
    
        val rectW = (bitmapWidth * scaleFactor).toInt()
        val rectH = (bitmapHeight * scaleFactor).toInt()
    
        val canvasCenter = Point(width / 2, height / 2)
        val left = canvasCenter.x - rectW / 2
        val top = canvasCenter.y - rectH / 2
        val right = canvasCenter.x + rectW / 2
        val bottom = canvasCenter.y + rectH / 2
    
        boundingRect = Rect(left, top, right, bottom)
    }
    
  2. 禁用缩放滤镜,保持像素锐利
    绘制Bitmap时使用关闭过滤的Paint,避免双线性过滤导致的像素模糊与拉伸:

    // 提前初始化绘制Bitmap的Paint
    private val bitmapPaint = Paint().apply {
        isFilterBitmap = false
        isAntiAlias = false
    }
    
    override fun onDraw(canvas: Canvas) {
        if (::drawingViewBitmap.isInitialized) {
            canvas.drawRect(boundingRect, PaintData.rectPaint)
            canvas.drawBitmap(drawingViewBitmap, null, boundingRect, bitmapPaint)
            drawGrid(canvas)
        }
    }
    
  3. 额外校验:匹配Bitmap密度
    确保drawingViewBitmap的密度与当前View的屏幕密度一致,避免系统自动密度缩放导致变形:

    Bitmap.createBitmap(bitmapWidth, bitmapHeight, Bitmap.Config.ARGB_8888).apply {
        density = resources.displayMetrics.densityDpi
    }
    

内容的提问来源于stack exchange,提问作者thebluepandabear

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.16 12:40:50