【问题标题】:Konvajs filter fill transparent areas within imageKonvajs 过滤器填充图像中的透明区域
【发布时间】:2018-08-27 12:12:09
【问题描述】:

konva 中是否有过滤器可以用白色填充图像的封闭透明区域?

This is the original image

This is a target image

我目前正在为边框使用 konva 自定义过滤器。 https://konvajs.github.io/docs/filters/Custom_Filter.html

var canvas, tempCanvas

function initBorderCanvas () {
  canvas = document.createElement('canvas')
  tempCanvas = document.createElement('canvas')
}

// make all pixells opaque 100% (except pixels that 100% transparent)
function removeTransparency (canvas) {
  var ctx = canvas.getContext('2d')

  var imageData = ctx.getImageData(0, 0, canvas.width, canvas.height)
  var nPixels = imageData.data.length
  for (var i = 3; i < nPixels; i += 4) {
    if (imageData.data[i] > 0) {
      imageData.data[i] = 255
    }
  }
  ctx.clearRect(0, 0, canvas.width, canvas.height)
  ctx.putImageData(imageData, 0, 0)
  return canvas
}

function Border (imageData) {
  var nPixels = imageData.data.length

  var ratio = this._cache.canvas.scene.pixelRatio
  var size = (this.getAttr('borderSize') || 0) * ratio

  if (size === 0) {
    return imageData
  }

  // - first set correct dimensions for canvases
  canvas.width = imageData.width
  canvas.height = imageData.height

  tempCanvas.width = imageData.width
  tempCanvas.height = imageData.height

  // - the draw original shape into temp canvas
  tempCanvas.getContext('2d').putImageData(imageData, 0, 0)

  // - then we need to remove alpha chanel, because it will affect shadow (transparent shapes has smaller shadow)
  removeTransparency(tempCanvas)

  var ctx = canvas.getContext('2d')
  var color = this.getAttr('borderColor') || 'black'

  // 3. we will use shadow as border
  // so we just need apply shadow on the original image
  ctx.save()
  ctx.shadowColor = color
  ctx.shadowBlur = size
  ctx.drawImage(tempCanvas, 0, 0)
  ctx.restore()

  // - Then we will dive in into image data of [original image + shadow]
  // and remove transparency from shadow
  var tempImageData = ctx.getImageData(0, 0, canvas.width, canvas.height)

  var SMOOTH_MIN_THRESHOLD = 0
  var SMOOTH_MAX_THRESHOLD = 0

  let val, hasValue

  var offset = 3

  for (var i = 3; i < nPixels; i += 4) {
    // skip opaque pixels
    if (imageData.data[i] === 255) {
      continue
    }

    val = tempImageData.data[i]
    hasValue = val !== 0
    if (!hasValue) {
      continue
    }
    if (val > SMOOTH_MAX_THRESHOLD) {
      val = 255
    } else if (val < SMOOTH_MIN_THRESHOLD) {
      val = 0
    } else {
      val =
        ((val - SMOOTH_MIN_THRESHOLD) /
          (SMOOTH_MAX_THRESHOLD - SMOOTH_MIN_THRESHOLD)) *
        255
    }
    tempImageData.data[i] = val
  }

  // draw resulted image (original + shadow without opacity) into canvas
  ctx.putImageData(tempImageData, 0, 0)

  // then fill whole image with color (after that shadow is colored)
  ctx.save()
  ctx.globalCompositeOperation = 'source-in'
  ctx.fillStyle = color
  ctx.fillRect(0, 0, canvas.width, canvas.height)
  ctx.restore()

  // then we need to copy colored shadow into original imageData
  var newImageData = ctx.getImageData(0, 0, canvas.width, canvas.height)

  var indexesToProcess = []
  for (var i = 3; i < nPixels; i += 4) {
    var hasTransparentOnTop =
      imageData.data[i - imageData.width * 4 * offset] === 0
    var hasTransparentOnTopRight =
      imageData.data[i - (imageData.width * 4 + 4) * offset] === 0
    var hasTransparentOnTopLeft =
      imageData.data[i - (imageData.width * 4 - 4) * offset] === 0
    var hasTransparentOnRight = imageData.data[i + 4 * offset] === 0
    var hasTransparentOnLeft = imageData.data[i - 4 * offset] === 0
    var hasTransparentOnBottom =
      imageData.data[i + imageData.width * 4 * offset] === 0
    var hasTransparentOnBottomRight =
      imageData.data[i + (imageData.width * 4 + 4) * offset] === 0
    var hasTransparentOnBottomLeft =
      imageData.data[i + (imageData.width * 4 - 4) * offset] === 0
    var hasTransparentAround =
      hasTransparentOnTop ||
      hasTransparentOnRight ||
      hasTransparentOnLeft ||
      hasTransparentOnBottom ||
      hasTransparentOnTopRight ||
      hasTransparentOnTopLeft ||
      hasTransparentOnBottomRight ||
      hasTransparentOnBottomLeft

    // if pixel presented in original image - skip it
    // because we need to change only shadow area
    if (
      imageData.data[i] === 255 ||
      (imageData.data[i] && !hasTransparentAround)
    ) {
      continue
    }
    if (!newImageData.data[i]) {
      // skip transparent pixels
      continue
    }
    indexesToProcess.push(i)
  }

  for (var index = 0; index < indexesToProcess.length; index += 1) {
    var i = indexesToProcess[index]

    var alpha = imageData.data[i] / 255

    imageData.data[i] = newImageData.data[i]
    imageData.data[i - 1] =
      newImageData.data[i - 1] * (1 - alpha) + imageData.data[i - 1] * alpha
    imageData.data[i - 2] =
      newImageData.data[i - 2] * (1 - alpha) + imageData.data[i - 2] * alpha
    imageData.data[i - 3] =
      newImageData.data[i - 3] * (1 - alpha) + imageData.data[i - 3] * alpha
  }
}

export { initBorderCanvas, Border }

它可以很好地在图像周围绘制边框。

Konva custom filter applied

有没有办法像目标图像一样填充圆圈?

这里是小提琴:http://jsfiddle.net/ecsy6hb4/34/

【问题讨论】:

    标签: konvajs


    【解决方案1】:

    最后我最终使用了https://github.com/sakri/MarchingSquaresJS。这是一种寻找轮廓点的算法。一旦我得到这些,我就会画出封闭的多边形并将其画在里面。这解决了我的问题。

    Outline points example

    这些红点是行进方块算法返回的轮廓点。

    小提琴:jsfiddle.net/ecsy6hb4/42/

    【讨论】:

      猜你喜欢
      • 2015-08-27
      • 1970-01-01
      • 1970-01-01
      • 2011-07-22
      • 1970-01-01
      • 2017-10-16
      • 2013-01-02
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多