/*
 * AXMIspGammaAlgorithm.cpp
 */

#include "AXMIspGammaAlgorithm.hpp"
#include <math.h>

AXMIspGammaAlgorithm::AXMIspGammaAlgorithm() {
	// TODO 自動生成されたコンストラクター・スタブ
}

AXMIspGammaAlgorithm::~AXMIspGammaAlgorithm() {
	// TODO Auto-generated destructor stub
}

/*
 * デフォルトのガンマカーブ生成
 */
bool AXMIspGammaAlgorithm::CreateDefaultGammaCurve(SHORT curvePointCnt, SHORT kneeBaseX, SHORT kneeBaseY, SHORT pixelMax, SHORT gammaValue, USHORT* vertex) {
	bool ret = false;

	if (gammaValue == 0) {
		return ret;
	}

	double reciprocal = (int)((RECIPROCAL_PRECISION + (gammaValue >> 1)) / gammaValue) / (double)RECIPROCAL_CUT_VALUE;		// γ逆数
	double kneeBaseXTmp = kneeBaseX / (double)KNEE_PRECISION;
	double kneeBaseYTmp = kneeBaseY / (double)KNEE_PRECISION;
	int vertexCnt = (int)(curvePointCnt * kneeBaseXTmp);

	// kneeBaseXポイントまでのガンマカーブ計算
	for (int i = 0; i < vertexCnt; i++) {
		vertex[i] = (SHORT)(pow(i, reciprocal) * (pixelMax * kneeBaseYTmp) / pow(curvePointCnt * kneeBaseXTmp, reciprocal));
	}

	// kneeBaseXポイントからのハイライト領域リニア計算
	SHORT linearStart = (SHORT)(pow(vertexCnt, reciprocal) * (pixelMax * kneeBaseYTmp) / pow(curvePointCnt * kneeBaseXTmp, reciprocal));
	if (vertexCnt == 0) {
		linearStart = 0;
	}
	CalcLinearInterpolation(vertexCnt, curvePointCnt, linearStart, pixelMax, &vertex[0]);

	return true;
}

/*
 * ガンマカーブの座標変換
 */
void AXMIspGammaAlgorithm::ConvertGammaCurve(SHORT curvePointCnt, const USHORT* vertex, SHORT kneeBaseX, SHORT kneeBaseY, SHORT kneeX, SHORT kneeY, SHORT pixelMax, USHORT* convVertex) {

	if (kneeBaseX <= 0) {
		return;
	}
	if (kneeBaseY <= 0) {
		return;
	}

	int vertexCnt = (int)(curvePointCnt * kneeBaseX / (double)KNEE_PRECISION);

	SHORT convStartIndex = 0;
	SHORT baseXPoint = 0;
	SHORT nextXPoint = 0;
	SHORT baseYPoint = 0;
	SHORT nextYPoint = 0;
	convVertex[0] = 0;
	convStartIndex++;
	// デフォルトガンマカーブの頂点座標分、線形補完を行う
	int offsetX = 0;
	if (kneeBaseX < kneeX) {
		offsetX = 1;
	}

	int convCnt = vertexCnt;
	if (convCnt + offsetX < curvePointCnt) {
		convCnt += offsetX;
	}

	for (SHORT i = 1; i < convCnt; i++) {
		nextXPoint = (SHORT)((kneeX * i * X_POINT_PRECISION + (kneeBaseX >> 1)) / kneeBaseX);
		baseYPoint = (SHORT)((kneeY * vertex[i-1] + (kneeBaseY >> 1)) / kneeBaseY);
		nextYPoint = (SHORT)((kneeY * vertex[i]   + (kneeBaseY >> 1)) / kneeBaseY);
		convStartIndex = CalcGammaCurvePoint(i, baseXPoint, nextXPoint, baseYPoint, nextYPoint, &convVertex[0], convStartIndex);
		baseXPoint = nextXPoint;
	}

	// 全ての座標が1未満に圧縮される場合は、0位置から線形補完を行う
	if (0 == nextXPoint / X_POINT_PRECISION) {
		convStartIndex = 0;
	}

	// kneeXポイントからのハイライト領域リニア計算
	SHORT linearStart = pixelMax;
	if (vertexCnt < curvePointCnt) {
		linearStart = (SHORT)((kneeY * vertex[vertexCnt] + (kneeBaseY >> 1)) / kneeBaseY);
	}
	CalcLinearInterpolation(convStartIndex, curvePointCnt, linearStart, pixelMax, &convVertex[0]);
}

/*
 * ガンマカーブの座標計算
 */
int AXMIspGammaAlgorithm::CalcGammaCurvePoint(SHORT baseCurvePntIndex, SHORT baseXPoint, SHORT nextXPoint, SHORT baseYPoint, SHORT nextYPoint, USHORT* convVertex, SHORT convStartIndex) {

	SHORT baseXPoint_ = (SHORT)(baseXPoint / X_POINT_PRECISION);
	SHORT nextXPoint_ = (SHORT)(nextXPoint / X_POINT_PRECISION);
	if (baseXPoint_ != nextXPoint_) {
		int pointwid = nextXPoint - baseXPoint;
		for (int i = baseXPoint_; i < nextXPoint_; i++) {
			SHORT vertex = (SHORT)(baseYPoint + ((nextYPoint - baseYPoint) * ((i + 1) * X_POINT_PRECISION - baseXPoint) + (pointwid >> 1)) / pointwid);
			if (vertex < 0) {
				vertex = 0;
			}
			convVertex[convStartIndex] = (USHORT)vertex;
			++convStartIndex;
		}
	}
	return convStartIndex;
}

/*
 * 線形補完座標計算
 */
void AXMIspGammaAlgorithm::CalcLinearInterpolation(SHORT linearStartIndex, SHORT linearEndIndex, SHORT linearStart, SHORT linearMaxValue, USHORT* convVertex) {

	SHORT linearWidth = linearEndIndex - linearStartIndex - 1;
	if (0 < linearWidth) {
		for (int i = 0; i <= linearWidth; i++) {
			SHORT vertex = (SHORT)(((linearMaxValue - linearStart) * i) / linearWidth + linearStart);
			if (vertex < 0) {
				vertex = 0;
			}
			convVertex[i + linearStartIndex] = (USHORT)vertex;
		}
	}
}
