about summary refs log tree commit diff
path: root/app/assets/javascripts/components/components/avatar.jsx
blob: f912d9a99d531ed0267667a35f50941651d7babd (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
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
import PureRenderMixin from 'react-addons-pure-render-mixin';

// From: http://stackoverflow.com/a/18320662
const resample = (canvas, width, height, resize_canvas) => {
  let width_source  = canvas.width;
  let height_source = canvas.height;
  width  = Math.round(width);
  height = Math.round(height);

  let ratio_w      = width_source / width;
  let ratio_h      = height_source / height;
  let ratio_w_half = Math.ceil(ratio_w / 2);
  let ratio_h_half = Math.ceil(ratio_h / 2);

  let ctx   = canvas.getContext("2d");
  let img   = ctx.getImageData(0, 0, width_source, height_source);
  let img2  = ctx.createImageData(width, height);
  let data  = img.data;
  let data2 = img2.data;

  for (let j = 0; j < height; j++) {
    for (let i = 0; i < width; i++) {
      let x2            = (i + j * width) * 4;
      let weight        = 0;
      let weights       = 0;
      let weights_alpha = 0;
      let gx_r          = 0;
      let gx_g          = 0;
      let gx_b          = 0;
      let gx_a          = 0;
      let center_y      = (j + 0.5) * ratio_h;
      let yy_start      = Math.floor(j * ratio_h);
      let yy_stop       = Math.ceil((j + 1) * ratio_h);

      for (let yy = yy_start; yy < yy_stop; yy++) {
        let dy       = Math.abs(center_y - (yy + 0.5)) / ratio_h_half;
        let center_x = (i + 0.5) * ratio_w;
        let w0       = dy * dy; //pre-calc part of w
        let xx_start = Math.floor(i * ratio_w);
        let xx_stop  = Math.ceil((i + 1) * ratio_w);

        for (let xx = xx_start; xx < xx_stop; xx++) {
          let dx = Math.abs(center_x - (xx + 0.5)) / ratio_w_half;
          let w  = Math.sqrt(w0 + dx * dx);

          if (w >= 1) {
            // pixel too far
            continue;
          }

          // hermite filter
          weight    = 2 * w * w * w - 3 * w * w + 1;
          let pos_x = 4 * (xx + yy * width_source);

          // alpha
          gx_a          += weight * data[pos_x + 3];
          weights_alpha += weight;

          // colors
          if (data[pos_x + 3] < 255)
            weight = weight * data[pos_x + 3] / 250;

          gx_r    += weight * data[pos_x];
          gx_g    += weight * data[pos_x + 1];
          gx_b    += weight * data[pos_x + 2];
          weights += weight;
        }
      }

      data2[x2]     = gx_r / weights;
      data2[x2 + 1] = gx_g / weights;
      data2[x2 + 2] = gx_b / weights;
      data2[x2 + 3] = gx_a / weights_alpha;
    }
  }

  // clear and resize canvas
  if (resize_canvas === true) {
    canvas.width  = width;
    canvas.height = height;
  } else {
    ctx.clearRect(0, 0, width_source, height_source);
  }

  // draw
  ctx.putImageData(img2, 0, 0);
};

const Avatar = React.createClass({

  propTypes: {
    src: React.PropTypes.string.isRequired,
    size: React.PropTypes.number.isRequired,
    style: React.PropTypes.object
  },

  getInitialState () {
    return {
      hovering: false
    };
  },

  mixins: [PureRenderMixin],

  handleMouseEnter () {
    this.setState({ hovering: true });
  },

  handleMouseLeave () {
    this.setState({ hovering: false });
  },

  handleLoad () {
    this.canvas.width  = this.image.naturalWidth;
    this.canvas.height = this.image.naturalHeight;
    this.canvas.getContext('2d').drawImage(this.image, 0, 0);

    resample(this.canvas, this.props.size * window.devicePixelRatio, this.props.size * window.devicePixelRatio, true);
  },

  setImageRef (c) {
    this.image = c;
  },

  setCanvasRef (c) {
    this.canvas = c;
  },

  render () {
    const { hovering } = this.state;

    return (
      <div onMouseEnter={this.handleMouseEnter} onMouseLeave={this.handleMouseLeave} style={{ ...this.props.style, width: `${this.props.size}px`, height: `${this.props.size}px`, position: 'relative' }}>
        <img ref={this.setImageRef} onLoad={this.handleLoad} src={this.props.src} width={this.props.size} height={this.props.size} alt='' style={{ position: 'absolute', top: '0', left: '0', opacity: hovering ? '1' : '0', borderRadius: '4px' }} />
        <canvas ref={this.setCanvasRef} style={{ borderRadius: '4px', width: this.props.size, height: this.props.size, opacity: hovering ? '0' : '1' }} />
      </div>
    );
  }

});

export default Avatar;