"use strict";
var mp = function(mp) {
mp.dye = {
getChannel: getChannel,
parseDyeString: parseDyeString,
asDyeString: asDyeString,
dyeImage: dyeImage
};
var channel = [null, "R", "G", "Y", "B", "M", "C", "W"];
/*
* Return the channel and intensity for the given RGB(A) array.
*/
function getChannel(color) {
var r = color[0], g = color[1], b = color[2],
max = Math.max(r, g, b);
if (max == 0) {
// Black
return { channel: null, intensity: 0 };
}
var min = Math.min(r, g, b), intensity = r + g + b;
var idx;
if (min != max && (min != 0 || (intensity != max && intensity != 2 * max))) {
// Not pure
idx = 0;
} else {
// Either all components are the same (r == b == g), two components are 0, or two components are the same with one as 0.
idx = (r != 0) | ((g != 0) << 1) | ((b != 0) << 2);
}
return { channel: channel[idx], intensity: max };
}
/*
* Return a dye specification from a dye string.
*/
function parseDyeString(dyeString) {
var channelStrings = dyeString.split(";");
var dyeData = {};
for (var i = 0; i < channelStrings.length; i++) {
var channelStr = channelStrings[i];
if (channelStr[1] != ":" || channelStr[2] != "#") {
console.error("parseDyeString: Expected ':#' in dyeString");
}
var channel = channelStr[0];
var parts = channelStr.substring(3).split(",");
var list = [];
for (var j = 0; j < parts.length; j++) {
list.push(mp.resource.parseColor(parts[j]));
}
dyeData[channel] = list;
}
return dyeData;
}
/*
* Return a dye string matching the given dye specification.
*/
function asDyeString(dye) {
var dyeString = "";
// skip null channel
for (var i = 1; i < channel.length; i++) {
var dyeChannel = channel[i];
var dyeParts = dye[dyeChannel];
if (!dyeParts || dyeParts.length == 0) {
continue;
}
dyeString += dyeChannel + ":#";
for (var j = 0; j < dyeParts.length; j++) {
var color = dyeParts[j];
dyeString += color[0].toString(16) + color[1].toString(16) + color[2].toString(16) + ",";
}
dyeString = dyeString.slice(0, -1);
dyeString += ";";
}
if (dyeString.length > 0) {
dyeString = dyeString.slice(0, -1);
}
return dyeString;
}
/*
* Dye the internal image data based on the specification provided by dyeData.
* The specification can be generated from a dyeString by parseDyeString.
* The array passed in will be modified.
*/
function dyeImage(imageData, dyeData) {
for (var p = 0; p < imageData.length; p += 4) {
var pixel = [imageData[p], imageData[p + 1], imageData[p + 2]];
var alpha = imageData[p + 3];
// Skip fully transparent pixels
if (!alpha) {
continue;
}
var channel = getChannel(pixel);
var channelId = channel.channel;
var intensity = channel.intensity;
// If this is an unknown dye channel, an empty dye channel, not a pure color, or black, skip it
if (!channelId || !(channelId in dyeData) || !dyeData[channelId].length || intensity == 0) {
continue;
}
// Scale the intensity from 0-255 to the palette size (i is the palette index, t is the remainder)
var val = intensity * dyeData[channelId].length
var i = Math.floor(val / 255);
var t = val - i * 255;
// If we exactly hit one of the palette colors, just use it
if (!t) {
--i;
imageData[p ] = dyeData[channelId][i][0];
imageData[p + 1] = dyeData[channelId][i][1];
imageData[p + 2] = dyeData[channelId][i][2];
continue;
}
// If we're between two palette colors, interpolate between them (the first color in a palette is implicitly black)
imageData[p ] = ((255 - t) * (i && dyeData[channelId][i - 1][0]) + t * dyeData[channelId][i][0]) / 255;
imageData[p + 1] = ((255 - t) * (i && dyeData[channelId][i - 1][1]) + t * dyeData[channelId][i][1]) / 255;
imageData[p + 2] = ((255 - t) * (i && dyeData[channelId][i - 1][2]) + t * dyeData[channelId][i][2]) / 255;
}
/* TODO */
return imageData;
}
return mp;
}(mp || {});