summaryrefslogtreecommitdiff
path: root/public/js/mp/dye.js
blob: e7af93845d78ca44806f874185b4bf932a471d27 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
"use strict";
var mp = function(mp) {
    mp.dye = {
        getChannel: getChannel,
        parseDyeString: parseDyeString,
        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 {
            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] != "#") {
                // TODO error
            }
            
            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;
    }
    
    /*
     * 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];
            if (!alpha) {
                continue;
            }

            var channel = getChannel(pixel);
            var channelId = channel.channel;

            if (!channelId || !(channelId in dyeData) || !dyeData[channelId].length) {
                continue;
            }

            var intensity = channel.intensity;
            var val = intensity * dyeData[channelId].length
            var i = Math.floor(val / 255);
            var t = val - i * 255;
            if (!t) {
                --i;
                imageData[p    ] = dyeData[channelId][i][0];
                imageData[p + 1] = dyeData[channelId][i][1];
                imageData[p + 2] = dyeData[channelId][i][2];
                continue;
            }

            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 || {});