Skip to content

Instantly share code, notes, and snippets.

@hexagit
Created November 21, 2018 06:43
Show Gist options
  • Save hexagit/1ccc50274c1ee60d70739623cc726984 to your computer and use it in GitHub Desktop.
Save hexagit/1ccc50274c1ee60d70739623cc726984 to your computer and use it in GitHub Desktop.
Lanczos法
using System;
using UnityEngine;
public partial class Bitmap
{
private const int lanczosDefaultN = 3;
private Bitmap resizeLanczos(uint width, uint height, uint range = lanczosDefaultN)
{
var bitmap = new Bitmap(width, height);
// スケール
var scaleX = (float)width / _width;
var scaleY = (float)height / _height;
// 単位を原画像の座標系に変換
var unitX = 1.0f / scaleX;
var unitY = 1.0f / scaleY;
// 原画像から参照する範囲
var n = (float)range;
var rangeX = n;
var rangeY = n;
// 1ピクセル幅を考慮した座標の補正
var correctionX = unitX * 0.5f;
var correctionY = unitY * 0.5f;
// 縮小のときは参照範囲を拡大
if(unitX > 1.0f) {
rangeX *= unitX;
}
if(unitY > 1.0f) {
rangeY *= unitY;
}
parallelFor(0, (int)bitmap._height, y => {
for(int x = 0; x < bitmap._width; ++x) {
// 原画像の座標系に変換
var srcX = unitX * x + correctionX;
var srcY = unitY * y + correctionY;
bitmap[x, y] = interpolateLanczos(srcX, srcY, rangeX, rangeY, n);
}
});
return bitmap;
}
private Color interpolateLanczos(float srcX, float srcY, float rangeX, float rangeY, float n)
{
float r = 0.0f;
float g = 0.0f;
float b = 0.0f;
float a = 0.0f;
// 距離のスケール修正
var scaleCorrectionX = n / rangeX;
var scaleCorrectionY = n / rangeY;
var totalWeight = 0.0f;
// 基準位置(srcX, srcY)から距離n未満のピクセル範囲を求める
int firstX = (int)Mathf.Clamp(srcX - rangeX + 0.5f, 0.0f, _width + 1);
int firstY = (int)Mathf.Clamp(srcY - rangeY + 0.5f, 0.0f, _height + 1);
int lastX = (int)Mathf.Clamp(srcX + rangeX - 0.5f, 0.0f, _width + 1);
int lastY = (int)Mathf.Clamp(srcY + rangeY - 0.5f, 0.0f, _height + 1);
// 1ピクセルずつ参照して距離パターンが限られるため重みをキャッシュ
var weightX = new float[lastX - firstX + 1];
var weightY = new float[lastY - firstY + 1];
// X方向の重みを事前計算
for(int intX = firstX; intX <= lastX; ++intX) {
// 着目ピクセルの中心と基準位置との距離から重み算出
var diff = (intX + 0.5f - srcX) * scaleCorrectionX;
weightX[intX - firstX] = lanczos(diff, n);
}
// Y方向の重みを事前計算
for(int intY = firstY; intY <= lastY; ++intY) {
// 着目ピクセルの中心と基準位置との距離から重み算出
var diff = (intY + 0.5f - srcY) * scaleCorrectionY;
weightY[intY - firstY] = lanczos(diff, n);
}
for(int intY = firstY; intY <= lastY; ++intY) {
for(int intX = firstX; intX <= lastX; ++intX) {
var weight = weightX[intX - firstX] * weightY[intY - firstY];
if(weight == 0.0f) continue;
totalWeight += weight;
var color = get(intX, intY);
// 完全透過部分のカラー影響を無視
if(color.a == 0.0f) continue;
r += color.r * weight;
g += color.g * weight;
b += color.b * weight;
a += color.a * weight;
}
}
var result = new Color(r, g, b, a);
// 積分なので全体で重み1となるよう補正
if(totalWeight != 0.0f) {
result = result / totalWeight;
}
return result;
}
private static float lanczos(float x, float n)
{
if(Math.Abs(x) >= n) return 0.0f;
return sinc(x) * sinc(x / n);
}
private static float sinc(float x)
{
if(x == 0.0f) return 1.0f;
return (float)(Math.Sin(Math.PI * x) / (Math.PI * x));
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment