-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathBlendmodes.class.php
361 lines (311 loc) · 17.8 KB
/
Blendmodes.class.php
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
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
<?php
class Blendmodes {
public function blend($baseImage, $topImage, $mode) {
$baseIsTrueColor = imageistruecolor($baseImage);
$topIsTrueColor = imageistruecolor($topImage);
$baseWidth = imagesx($baseImage);
$baseHeight = imagesy($baseImage);
$topWidth = imagesx($topImage);
$topHeight = imagesy($topImage);
$destX = ($baseWidth - $topWidth) / 2;
$destY = ($baseHeight - $topHeight) / 2;
for ($x = 0; $x < $topWidth; ++$x) {
for ($y = 0; $y < $topHeight; ++$y) {
$color = imagecolorat($baseImage, $x + $destX, $y + $destY);
if ($baseIsTrueColor) {
$baseColor = array(
'red' => ($color >> 16) & 0xFF,
'green' => ($color >> 8) & 0xFF,
'blue' => $color & 0xFF,
'alpha' => ($color & 0x7F000000) >> 24,
);
} else {
$baseColor = imagecolorsforindex($baseImage, $color);
}
$color = imagecolorat($topImage, $x, $y);
if ($topIsTrueColor) {
$topColor = array(
'red' => ($color >> 16) & 0xFF,
'green' => ($color >> 8) & 0xFF,
'blue' => $color & 0xFF,
'alpha' => ($color & 0x7F000000) >> 24,
);
} else {
$topColor = imagecolorsforindex($topImage, $color);
}
switch ($mode) {
case 'dissolve':
$topOpacityPercent = round(($topColor['alpha'] / 127) * 100);
$randomPercent = rand(1,100);
if ($randomPercent <= $topOpacityPercent) {
$destColorAlpha = 127.0;
} else {
$destColorAlpha = 0.0;
}
$destColor = array(
'red' => intval($topColor['red']),
'green' => intval($topColor['green']),
'blue' => intval($topColor['blue']),
'alpha' => intval($destColorAlpha)
);
break;
case 'darkerColor':
$baseColorHsl = $this->rgbToHsl($baseColor['red'], $baseColor['green'], $baseColor['green']);
$topColorHsl = $this->rgbToHsl($topColor['red'], $topColor['green'], $topColor['blue']);
if ($baseColorHsl['lightness'] < $topColorHsl['lightness']) {
$destColor = array(
'red' => intval($baseColor['red']),
'green' => intval($baseColor['green']),
'blue' => intval($baseColor['blue']),
'alpha' => intval($baseColor['alpha'])
);
} else {
$destColor = array(
'red' => intval($topColor['red']),
'green' => intval($topColor['green']),
'blue' => intval($topColor['blue']),
'alpha' => intval($topColor['alpha'])
);
}
break;
case 'darken':
$destColor = array(
'red' => intval(min($baseColor['red'], $topColor['red'])),
'green' => intval(min($baseColor['green'], $topColor['green'])),
'blue' => intval(min($baseColor['blue'], $topColor['blue'])),
'alpha' => intval($topColor['alpha'])
);
break;
default:
case 'multiply':
$destColor = array(
'red' => intval($baseColor['red'] * ($topColor['red'] / 255.0)),
'green' => intval($baseColor['green'] * ($topColor['green'] / 255.0)),
'blue' => intval($baseColor['blue'] * ($topColor['blue'] / 255.0)),
'alpha' => intval($topColor['alpha'])
);
break;
case 'colorBurn':
$destColor = array(
'red' => intval($baseColor['red'] * ($topColor['red'] / 255.0)),
'green' => intval($baseColor['green'] * ($topColor['green'] / 255.0)),
'blue' => intval($baseColor['blue'] * ($topColor['blue'] / 255.0)),
'alpha' => intval($topColor['alpha'])
);
break;
case 'linearBurn':
$destColor = array(
'red' => intval($baseColor['red'] + $topColor['red'] - 255.0),
'green' => intval($baseColor['green'] + $topColor['green'] - 255.0),
'blue' => intval($baseColor['blue'] + $topColor['blue'] - 255.0),
'alpha' => intval($topColor['alpha'])
);
break;
case 'lighterColor':
$baseColorHsl = $this->rgbToHsl($baseColor['red'], $baseColor['green'], $baseColor['green']);
$topColorHsl = $this->rgbToHsl($topColor['red'], $topColor['green'], $topColor['blue']);
if ($baseColorHsl['lightness'] > $topColorHsl['lightness']) {
$destColor = array(
'red' => intval($baseColor['red']),
'green' => intval($baseColor['green']),
'blue' => intval($baseColor['blue']),
'alpha' => intval($baseColor['alpha'])
);
} else {
$destColor = array(
'red' => intval($topColor['red']),
'green' => intval($topColor['green']),
'blue' => intval($topColor['blue']),
'alpha' => intval($topColor['alpha'])
);
}
break;
case 'lighten':
$destColor = array(
'red' => intval(max($baseColor['red'], $topColor['red'])),
'green' => intval(max($baseColor['green'], $topColor['green'])),
'blue' => intval(max($baseColor['blue'], $topColor['blue'])),
'alpha' => intval($topColor['alpha'])
);
break;
case 'screen':
$destColor = array(
'red' => intval(round($baseColor['red'] + ((255.0 - $baseColor['red']) / 100) * (($topColor['red'] / 255.0) * 100))),
'green' => intval(round($baseColor['green'] + ((255.0 - $baseColor['green']) / 100) * (($topColor['green'] / 255.0) * 100))),
'blue' => intval(round($baseColor['blue'] + ((255.0 - $baseColor['blue']) / 100) * (($topColor['blue'] / 255.0) * 100))),
'alpha' => intval($topColor['alpha'])
);
break;
case 'colorDodge':
$destColor = array(
'red' => intval(($baseColor['red'] + $topColor['red'] > 255.0 ? 255.0 : $baseColor['red'])),
'green' => intval(($baseColor['green'] + $topColor['green'] > 255.0 ? 255.0 : $baseColor['green'])),
'blue' => intval(($baseColor['blue'] + $topColor['blue'] > 255.0 ? 255.0 : $baseColor['blue'])),
'alpha' => intval($topColor['alpha'])
);
break;
case 'linearDodge':
$destColor = array(
'red' => intval(($baseColor['red'] + $topColor['red']) > 255.0 ? 255.0 : ($baseColor['red'] + $topColor['red'])),
'green' => intval(($baseColor['green'] + $topColor['green']) > 255.0 ? 255.0 : ($baseColor['green'] + $topColor['red'])),
'blue' => intval(($baseColor['blue'] + $topColor['blue']) > 255.0 ? 255.0 : ($baseColor['blue'] + $topColor['blue'])),
'alpha' => intval($topColor['alpha'])
);
break;
case 'overlay':
$destColor = array(
'red' => intval($baseColor['red'] > 127.5 ? (($topColor['red'] * ((255.0 - $baseColor['red']) / 127.5)) + ($baseColor['red'] - (255.0 - $baseColor['red']))) : ($topColor['red'] * ($baseColor['red'] / 127.5))),
'green' => intval($baseColor['green'] > 127.5 ? (($topColor['green'] * ((255.0 - $baseColor['green']) / 127.5)) + ($baseColor['green'] - (255.0 - $baseColor['green']))) : ($topColor['green'] * ($baseColor['green'] / 127.5))),
'blue' => intval($baseColor['blue'] > 127.5 ? (($topColor['blue'] * ((255.0 - $baseColor['blue']) / 127.5)) + ($baseColor['blue'] - (255.0 - $baseColor['blue']))) : ($topColor['blue'] * ($baseColor['blue'] / 127.5))),
'alpha' => intval($topColor['alpha'])
);
break;
case 'softLight':
$destColor = array(
'red' => intval(round((($topColor['red'] / 255.0) < 0.5) ? ((2 * ($baseColor['red'] / 255.0) * ($topColor['red'] / 255.0) + (($baseColor['red'] / 255.0) * ($baseColor['red'] / 255.0)) * (1 - (2 * ($topColor['red'] / 255.0)))) * 255.0) : (((2 * ($baseColor['red'] / 255.0)) * (1-($topColor['red'] / 255.0)) + sqrt(($baseColor['red'] / 255.0)) * ((2 * ($topColor['red'] / 255.0)) - 1)) * 255.0))),
'green' => intval(round((($topColor['green'] / 255.0) < 0.5) ? ((2 * ($baseColor['green'] / 255.0) * ($topColor['green'] / 255.0) + (($baseColor['green'] / 255.0) * ($baseColor['green'] / 255.0)) * (1 - (2 * ($topColor['green'] / 255.0)))) * 255.0) : (((2 * ($baseColor['green'] / 255.0)) * (1-($topColor['green'] / 255.0)) + sqrt(($baseColor['green'] / 255.0)) * ((2 * ($topColor['green'] / 255.0)) - 1)) * 255.0))),
'blue' => intval(round((($topColor['blue'] / 255.0) < 0.5) ? ((2 * ($baseColor['blue'] / 255.0) * ($topColor['blue'] / 255.0) + (($baseColor['blue'] / 255.0) * ($baseColor['blue'] / 255.0)) * (1 - (2 * ($topColor['blue'] / 255.0)))) * 255.0) : (((2 * ($baseColor['blue'] / 255.0)) * (1-($topColor['blue'] / 255.0)) + sqrt(($baseColor['blue'] / 255.0)) * ((2 * ($topColor['blue'] / 255.0)) - 1)) * 255.0))),
'alpha' => intval($topColor['alpha'])
);
break;
case 'hardLight':
$destColor = array(
'red' => intval(round(($topColor['red'] > 127.5) ? ($baseColor['red'] * ((255 - $topColor['red']) / 127.5)) + ($topColor['red'] - (255 - $topColor['red'])) : ($baseColor['red'] * ($topColor['red'] / 127.5)))),
'green' => intval(round(($topColor['green'] > 127.5) ? ($baseColor['green'] * ((255 - $topColor['green']) / 127.5)) + ($topColor['green'] - (255 - $topColor['green'])) : ($baseColor['green'] * ($topColor['green'] / 127.5)))),
'blue' => intval(round(($topColor['blue'] > 127.5) ? ($baseColor['blue'] * ((255 - $topColor['blue']) / 127.5)) + ($topColor['blue'] - (255 - $topColor['blue'])) : ($baseColor['blue'] * ($topColor['blue'] / 127.5)))),
'alpha' => intval($topColor['alpha'])
);
break;
case 'vividLight':
// hotfix for division by zero bug
foreach ($baseColor as $baseKey => $base) if ($baseKey != 'alpha') if ($base <= 0) {
$baseColor[$baseKey] = 1.0;
} elseif ($base >= 255.0) {
$baseColor[$baseKey] = 254.0;
}
foreach ($topColor as $topKey => $top) if ($topKey != 'alpha') if ($top <= 0) {
$topColor[$topKey] = 1.0;
} elseif ($top >= 255.0) {
$topColor[$topKey] = 254.0;
}
$destColor = array(
'red' => intval(round((($topColor['red'] / 255) <= 0.5) ? ((1 - (1 - ($baseColor['red'] / 255.0)) / (2 * ($topColor['red'] / 255.0))) * 255.0) : ((($baseColor['red'] / 255.0) / (2 * (1 - ($topColor['red'] / 255.0)))) * 255.0))),
'green' => intval(round((($topColor['green'] / 255) <= 0.5) ? ((1 - (1 - ($baseColor['green'] / 255.0)) / (2 * ($topColor['green'] / 255.0))) * 255.0) : ((($baseColor['green'] / 255.0) / (2 * (1 - ($topColor['green'] / 255.0)))) * 255.0))),
'blue' => intval(round((($topColor['blue'] / 255) <= 0.5) ? ((1 - (1 - ($baseColor['blue'] / 255.0)) / (2 * ($topColor['blue'] / 255.0))) * 255.0) : ((($baseColor['blue'] / 255.0) / (2 * (1 - ($topColor['blue'] / 255.0)))) * 255.0))),
'alpha' => intval($topColor['alpha'])
);
break;
case 'linearLight':
$destColor = array(
'red' => intval(round(((($baseColor['red']/255) + (2 * ($topColor['red']/255)) - 1) * 255.0))),
'green' => intval(round(((($baseColor['green']/255) + (2 * ($topColor['green']/255)) - 1) * 255.0))),
'blue' => intval(round(((($baseColor['blue']/255) + (2 * ($topColor['blue']/255)) - 1) * 255.0))),
'alpha' => intval($topColor['alpha'])
);
break;
case 'pinLight':
$destColor = array(
'red' => intval(round((($baseColor['red'] / 255.0) < ((2 * ($topColor['red'] / 255.0)) - 1) ) ? (((2 * ($topColor['red'] / 255.0)) - 1) * 255.0) : ( (2 * ($topColor['red'] / 255.0) - 1 < ($baseColor['red'] / 255.0)) && (($baseColor['red'] / 255.0) < 2 * ($topColor['red'] / 255.0))) ? ($baseColor['red']) : ((2 * ($topColor['red'] / 255.0)) * 255.0))),
'green' => intval(round((($baseColor['green'] / 255.0) < ((2 * ($topColor['green'] / 255.0)) - 1) ) ? (((2 * ($topColor['green'] / 255.0)) - 1) * 255.0) : ( (2 * ($topColor['green'] / 255.0) - 1 < ($baseColor['green'] / 255.0)) && (($baseColor['green'] / 255.0) < 2 * ($topColor['green'] / 255.0))) ? ($baseColor['green']) : ((2 * ($topColor['green'] / 255.0)) * 255.0))),
'blue' => intval(round((($baseColor['blue'] / 255.0) < ((2 * ($topColor['blue'] / 255.0)) - 1) ) ? (((2 * ($topColor['blue'] / 255.0)) - 1) * 255.0) : ( (2 * ($topColor['blue'] / 255.0) - 1 < ($baseColor['blue'] / 255.0)) && (($baseColor['blue'] / 255.0) < 2 * ($topColor['blue'] / 255.0))) ? ($baseColor['blue']) : ((2 * ($topColor['blue'] / 255.0)) * 255.0))),
'alpha' => intval($topColor['alpha'])
);
break;
case 'hardMix':
$destColor = array(
'red' => intval(($baseColor['red'] + $topColor['red']) >= 255.0 ? 255.0 : 0.0),
'green' => intval(($baseColor['green'] + $topColor['green']) >= 255.0 ? 255.0 : 0.0),
'blue' => intval(($baseColor['blue'] + $topColor['blue']) >= 255.0 ? 255.0 : 0.0),
'alpha' => intval($topColor['alpha'])
);
break;
case 'difference':
$destColor = array(
'red' => intval(($topColor['red'] - $baseColor['red']) < 0 ? (($topColor['red'] - $baseColor['red']) * -1) : ($topColor['red'] - $baseColor['red'])),
'green' => intval(($topColor['green'] - $baseColor['green']) < 0 ? (($topColor['green'] - $baseColor['green']) * -1) : ($topColor['green'] - $baseColor['green'])),
'blue' => intval(($topColor['blue'] - $baseColor['blue']) < 0 ? (($topColor['blue'] - $baseColor['blue']) * -1) : ($topColor['blue'] - $baseColor['blue'])),
'alpha' => intval($topColor['alpha'])
);
break;
case 'exclusion':
$destColor = array(
'red' => intval(round(((0.5 - 2.0 * (($baseColor['red'] / 255.0) - 0.5) * (($topColor['red'] / 255.0) - 0.5)) * 255.0))),
'green' => intval(round(((0.5 - 2.0 * (($baseColor['green'] / 255.0) - 0.5) * (($topColor['green'] / 255.0) - 0.5)) * 255.0))),
'blue' => intval(round(((0.5 - 2.0 * (($baseColor['blue'] / 255.0) - 0.5) * (($topColor['blue'] / 255.0) - 0.5)) * 255.0))),
'alpha' => intval($topColor['alpha'])
);
break;
case 'subtract':
$destColor = array(
'red' => intval(($baseColor['red'] - $topColor['red']) < 0.0 ? 0.0 : ($baseColor['red'] - $topColor['red'])),
'green' => intval(($baseColor['green'] - $topColor['green']) < 0.0 ? 0.0 : ($baseColor['green'] - $topColor['green'])),
'blue' => intval(($baseColor['blue'] - $topColor['blue']) < 0.0 ? 0.0 : ($baseColor['blue'] - $topColor['blue'])),
'alpha' => intval($topColor['alpha'])
);
break;
case 'divide':
// hotfix for division by zero bug
foreach ($baseColor as $baseKey => $base) if ($baseKey != 'alpha') if ($base <= 0) $baseColor[$baseKey] = 1;
foreach ($topColor as $topKey => $top) if ($topKey != 'alpha') if ($top <= 0) $topColor[$topKey] = 1;
$destColor = array(
'red' => intval((($baseColor['red'] / $topColor['red']) * 255.0) > 255.0 ? 255.0 : (($baseColor['red'] / $topColor['red']) * 255.0)),
'green' => intval((($baseColor['green'] / $topColor['green']) * 255.0) > 255.0 ? 255.0 : (($baseColor['green'] / $topColor['green']) * 255.0)),
'blue' => intval((($baseColor['blue'] / $topColor['blue']) * 255.0) > 255.0 ? 255.0 : (($baseColor['blue'] / $topColor['blue']) * 255.0)),
'alpha' => intval($topColor['alpha'])
);
break;
case 'hue':
$baseColorHsl = $this->rgbToHsl($baseColor['red'], $baseColor['green'], $baseColor['green']);
$topColorHsl = $this->rgbToHsl($topColor['red'], $topColor['green'], $topColor['blue']);
$destColorRgb = $this->hslToRgb($topColorHsl['hue'], $baseColorHsl['saturation'], $baseColorHsl['lightness']);
$destColor = array(
'red' => $destColorRgb['red'],
'green' => $destColorRgb['green'],
'blue' => $destColorRgb['blue'],
'alpha' => $topColor['alpha']
);
break;
case 'saturation':
$baseColorHsl = $this->rgbToHsl($baseColor['red'], $baseColor['green'], $baseColor['green']);
$topColorHsl = $this->rgbToHsl($topColor['red'], $topColor['green'], $topColor['blue']);
$destColorRgb = $this->hslToRgb($baseColorHsl['hue'], $topColorHsl['saturation'], $baseColorHsl['lightness']);
$destColor = array(
'red' => $destColorRgb['red'],
'green' => $destColorRgb['green'],
'blue' => $destColorRgb['blue'],
'alpha' => $topColor['alpha']
);
break;
case 'color':
$baseColorHsl = $this->rgbToHsl($baseColor['red'], $baseColor['green'], $baseColor['green']);
$topColorHsl = $this->rgbToHsl($topColor['red'], $topColor['green'], $topColor['blue']);
$destColorRgb = $this->hslToRgb($topColorHsl['hue'], $topColorHsl['saturation'], $baseColorHsl['lightness']);
$destColor = array(
'red' => $destColorRgb['red'],
'green' => $destColorRgb['green'],
'blue' => $destColorRgb['blue'],
'alpha' => $topColor['alpha']
);
break;
case 'luminosity':
$baseColorHsl = $this->rgbToHsl($baseColor['red'], $baseColor['green'], $baseColor['green']);
$topColorHsl = $this->rgbToHsl($topColor['red'], $topColor['green'], $topColor['blue']);
$destColorRgb = $this->hslToRgb($baseColorHsl['hue'], $baseColorHsl['saturation'], $topColorHsl['lightness']);
$destColor = array(
'red' => $destColorRgb['red'],
'green' => $destColorRgb['green'],
'blue' => $destColorRgb['blue'],
'alpha' => $topColor['alpha']
);
break;
}
$colorIndex = imagecolorallocatealpha($baseImage, $destColor['red'], $destColor['green'], $destColor['blue'], $destColor['alpha']);
if ($colorIndex === false) {
$colorIndex = imagecolorclosestalpha($baseImage, $destColor['red'], $destColor['green'], $destColor['blue'], $destColor['alpha']);
}
imagesetpixel($baseImage, $x + $destX, $y + $destY, $colorIndex);
}
}
return $baseImage;
}
}