Skip to content

Commit f095a81

Browse files
author
Chad Little
committed
Allow custom image generation when choosing a profile image
Summary: Ref T10319. This swaps the default in the Picture Chooser to allow picking of the custom unique avatar. We're currently going with 100k unique possibilities. The logic roughly hashes a user name and picks an image pack, color, and border. Based on that, we select the first character of their username, or fall back to Psyduck if not [a-z][0-9]. Test Plan: Set the following usernames from ProfilePicture as a test: chad, epriestley, sally, 007, _cat_, -doggie-. {F3453979} Reviewers: epriestley Reviewed By: epriestley Subscribers: Korvin Maniphest Tasks: T10319 Differential Revision: https://secure.phabricator.com/D17430
1 parent 8ce2583 commit f095a81

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

130 files changed

+212
-52
lines changed

resources/builtin/alphanumeric/Cd.png

-1.42 KB
Binary file not shown.

resources/builtin/alphanumeric/Dd.png

-1.19 KB
Binary file not shown.

resources/builtin/alphanumeric/Gd.png

-1.46 KB
Binary file not shown.

resources/builtin/alphanumeric/I.png

-211 Bytes
Binary file not shown.

resources/builtin/alphanumeric/Id.png

-212 Bytes
Binary file not shown.

resources/builtin/alphanumeric/L.png

-215 Bytes
Binary file not shown.

resources/builtin/alphanumeric/M.png

-1.56 KB
Binary file not shown.

resources/builtin/alphanumeric/Md.png

-1.56 KB
Binary file not shown.

resources/builtin/alphanumeric/R.png

-1.24 KB
Binary file not shown.

resources/builtin/alphanumeric/Rd.png

-1.24 KB
Binary file not shown.
-1.14 KB
Binary file not shown.
-1.14 KB
Binary file not shown.

resources/builtin/alphanumeric/Ud.png

-1.08 KB
Binary file not shown.

resources/builtin/alphanumeric/V.png

-1.46 KB
Binary file not shown.

resources/builtin/alphanumeric/Vd.png

-1.46 KB
Binary file not shown.
1.55 KB
835 Bytes
1.54 KB
1016 Bytes
1.27 KB
813 Bytes
1.72 KB
1.53 KB
1.43 KB
1.33 KB
1.44 KB
1.29 KB
703 Bytes
561 Bytes
1.48 KB
815 Bytes
634 Bytes
857 Bytes
1.32 KB
688 Bytes
1.72 KB
1.44 KB
1.71 KB
1.08 KB
1.82 KB
1.36 KB
1.45 KB
719 Bytes
1.18 KB
1.41 KB
2.33 KB
1.51 KB
1.22 KB
995 Bytes
1.41 KB
1.19 KB
1.45 KB
211 Bytes
1.56 KB
1.23 KB
1.07 KB
1.46 KB
211 Bytes
216 Bytes
1.56 KB
1.23 KB
1.46 KB

src/applications/files/builtin/PhabricatorFilesComposeAvatarBuiltinFile.php

Lines changed: 139 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ public function getBorder() {
3737
public function getBuiltinFileKey() {
3838
$icon = $this->getIcon();
3939
$color = $this->getColor();
40-
$border = $this->getBorder();
40+
$border = implode(',', $this->getBorder());
4141
$desc = "compose(icon={$icon}, color={$color}, border={$border}";
4242
$hash = PhabricatorHash::digestToLength($desc, 40);
4343
return "builtin:{$hash}";
@@ -46,7 +46,7 @@ public function getBuiltinFileKey() {
4646
public function getBuiltinDisplayName() {
4747
$icon = $this->getIcon();
4848
$color = $this->getColor();
49-
$border = $this->getBorder();
49+
$border = implode(',', $this->getBorder());
5050
return "{$icon}-{$color}-{$border}.png";
5151
}
5252

@@ -55,30 +55,160 @@ public function loadBuiltinFileData() {
5555
$this->getColor(), $this->getIcon(), $this->getBorder());
5656
}
5757

58-
private function composeImage($color, $icon, $border) {
59-
// TODO
58+
private function composeImage($color, $image, $border) {
59+
$color_const = hexdec(trim($color, '#'));
60+
$true_border = self::rgba2gd($border);
61+
$image_map = self::getImageMap();
62+
$data = Filesystem::readFile($image_map[$image]);
63+
64+
$img = imagecreatefromstring($data);
65+
66+
// 4 pixel border at 50x50, 32 pixel border at 400x400
67+
$canvas = imagecreatetruecolor(400, 400);
68+
69+
$image_fill = imagefill($canvas, 0, 0, $color_const);
70+
if (!$image_fill) {
71+
throw new Exception(
72+
pht('Failed to save builtin avatar image data (imagefill).'));
73+
}
74+
75+
$border_thickness = imagesetthickness($canvas, 64);
76+
if (!$border_thickness) {
77+
throw new Exception(
78+
pht('Failed to save builtin avatar image data (imagesetthickness).'));
79+
}
80+
81+
$image_rectangle = imagerectangle($canvas, 0, 0, 400, 400, $true_border);
82+
if (!$image_rectangle) {
83+
throw new Exception(
84+
pht('Failed to save builtin avatar image data (imagerectangle).'));
85+
}
86+
87+
$image_copy = imagecopy($canvas, $img, 0, 0, 0, 0, 400, 400);
88+
if (!$image_copy) {
89+
throw new Exception(
90+
pht('Failed to save builtin avatar image data (imagecopy).'));
91+
}
92+
93+
return PhabricatorImageTransformer::saveImageDataInAnyFormat(
94+
$canvas,
95+
'image/png');
96+
}
97+
98+
private static function rgba2gd($rgba) {
99+
$r = $rgba[0];
100+
$g = $rgba[1];
101+
$b = $rgba[2];
102+
$a = $rgba[3];
103+
$a = (1 - $a) * 255;
104+
return ($a << 24) | ($r << 16) | ($g << 8) | $b;
60105
}
61106

62107
public static function getImageMap() {
63108
$root = dirname(phutil_get_library_root('phabricator'));
64109
$root = $root.'/resources/builtin/alphanumeric/';
65110

66111
$map = array();
67-
$list = Filesystem::listDirectory($root, $include_hidden = false);
112+
$list = id(new FileFinder($root))
113+
->withType('f')
114+
->withFollowSymlinks(true)
115+
->find();
116+
68117
foreach ($list as $file) {
69-
$key = 'alphanumeric/'.$file;
70-
$map[$key] = $root.$file;
118+
$map['alphanumeric/'.$file] = $root.$file;
119+
}
120+
return $map;
121+
}
122+
123+
public function getUniqueProfileImage($username) {
124+
$pack_map = $this->getImagePackMap();
125+
$image_map = $this->getImageMap();
126+
$color_map = $this->getColorMap();
127+
$border_map = $this->getBorderMap();
128+
$file = phutil_utf8_strtoupper(substr($username, 0, 1));
129+
130+
$pack_count = count($pack_map);
131+
$color_count = count($color_map);
132+
$border_count = count($border_map);
133+
134+
$pack_seed = $username.'_pack';
135+
$color_seed = $username.'_color';
136+
$border_seed = $username.'_border';
137+
138+
$pack_key =
139+
PhabricatorHash::digestToRange($pack_seed, 1, $pack_count);
140+
$color_key =
141+
PhabricatorHash::digestToRange($color_seed, 1, $color_count);
142+
$border_key =
143+
PhabricatorHash::digestToRange($border_seed, 1, $border_count);
144+
145+
$pack = $pack_map[$pack_key];
146+
$icon = 'alphanumeric/'.$pack.'/'.$file.'.png';
147+
$color = $color_map[$color_key];
148+
$border = $border_map[$border_key];
149+
150+
if (!isset($image_map[$icon])) {
151+
$icon = 'alphanumeric/'.$pack.'/_default.png';
71152
}
72153

154+
return array('color' => $color, 'icon' => $icon, 'border' => $border);
155+
}
156+
157+
public function getUserProfileImageFile($username) {
158+
$unique = $this->getUniqueProfileImage($username);
159+
160+
$composer = id(new self())
161+
->setIcon($unique['icon'])
162+
->setColor($unique['color'])
163+
->setBorder($unique['border']);
164+
165+
$data = $composer->loadBuiltinFileData();
166+
167+
$unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();
168+
$file = PhabricatorFile::newFromFileData(
169+
$data,
170+
array(
171+
'name' => $composer->getBuiltinDisplayName(),
172+
'profile' => true,
173+
'canCDN' => true,
174+
));
175+
unset($unguarded);
176+
177+
return $file;
178+
}
179+
180+
public static function getImagePackMap() {
181+
$root = dirname(phutil_get_library_root('phabricator'));
182+
$root = $root.'/resources/builtin/alphanumeric/';
183+
184+
$map = id(new FileFinder($root))
185+
->withType('d')
186+
->withFollowSymlinks(false)
187+
->find();
188+
189+
return $map;
190+
}
191+
192+
public static function getBorderMap() {
193+
194+
$map = array(
195+
array(0, 0, 0, 0),
196+
array(0, 0, 0, 0.3),
197+
array(255, 255, 255, 0.4),
198+
array(255, 255, 255, 0.7),
199+
);
200+
73201
return $map;
74202
}
75203

76204
public static function getColorMap() {
205+
//
206+
// Generated Colors
207+
// http://tools.medialab.sciences-po.fr/iwanthue/
208+
//
77209
$map = array(
78210
'#335862',
79-
'#dfc47b',
80211
'#2d5192',
81-
'#c0bc6e',
82212
'#3c5da0',
83213
'#99cd86',
84214
'#704889',
@@ -91,19 +221,14 @@ public static function getColorMap() {
91221
'#4bd0e3',
92222
'#a25542',
93223
'#4eb4f3',
94-
'#705412',
95224
'#6da8ec',
96225
'#545608',
97226
'#829ce5',
98227
'#68681d',
99228
'#607bc2',
100-
'#d1b66e',
101229
'#4b69ad',
102-
'#a4a154',
103230
'#236ead',
104-
'#daa969',
105231
'#31a0de',
106-
'#996f31',
107232
'#4f8ed0',
108233
'#846f2a',
109234
'#bdb0f0',
@@ -121,13 +246,11 @@ public static function getColorMap() {
121246
'#7f4c7f',
122247
'#a1bb7a',
123248
'#65558f',
124-
'#c2a962',
125249
'#445082',
126250
'#c9ca8e',
127251
'#265582',
128252
'#f4b189',
129253
'#265582',
130-
'#bd8f50',
131254
'#40b8e1',
132255
'#814a28',
133256
'#80c8f6',
@@ -142,7 +265,6 @@ public static function getColorMap() {
142265
'#b888c9',
143266
'#476025',
144267
'#9987c5',
145-
'#828136',
146268
'#7867a3',
147269
'#769b5a',
148270
'#c46e9d',
@@ -161,7 +283,6 @@ public static function getColorMap() {
161283
'#45a998',
162284
'#faa38c',
163285
'#265582',
164-
'#ad954f',
165286
'#265582',
166287
'#e4b788',
167288
'#265582',
@@ -187,7 +308,6 @@ public static function getColorMap() {
187308
'#ae78ad',
188309
'#569160',
189310
'#d898be',
190-
'#525620',
191311
'#8eb4e8',
192312
'#5e622c',
193313
'#929ad3',
@@ -209,13 +329,9 @@ public static function getColorMap() {
209329
'#63acda',
210330
'#7b5d30',
211331
'#66bed6',
212-
'#a66c4e',
213332
'#3585b0',
214-
'#ba865c',
215333
'#5880b0',
216-
'#9b864d',
217334
'#739acc',
218-
'#9d764a',
219335
'#48a3ba',
220336
'#9d565b',
221337
'#7fc4ca',
@@ -232,15 +348,11 @@ public static function getColorMap() {
232348
'#6bafb6',
233349
'#8c5744',
234350
'#84b9d6',
235-
'#725238',
236351
'#9db3d6',
237-
'#816f3e',
238352
'#777cad',
239-
'#a6a86e',
240353
'#826693',
241354
'#86a779',
242355
'#9d7fad',
243-
'#8b8e55',
244356
'#b193c2',
245357
'#547348',
246358
'#d5adcb',
@@ -250,11 +362,9 @@ public static function getColorMap() {
250362
'#b2add6',
251363
'#5a623d',
252364
'#9793bb',
253-
'#bea975',
254365
'#3c5472',
255366
'#d5c5a1',
256367
'#5e5a7f',
257-
'#b09c68',
258368
'#2c647e',
259369
'#d8b194',
260370
'#49607f',
@@ -269,7 +379,6 @@ public static function getColorMap() {
269379
'#ad697e',
270380
'#799a6d',
271381
'#916b88',
272-
'#aeb68d',
273382
'#69536b',
274383
'#b4c4ad',
275384
'#845865',
@@ -291,20 +400,16 @@ public static function getColorMap() {
291400
'#50959b',
292401
'#b27d7a',
293402
'#335862',
294-
'#b2a381',
295403
'#335862',
296404
'#bcadc4',
297405
'#706343',
298406
'#749ebc',
299407
'#8c6a50',
300408
'#92b8c4',
301-
'#a07d62',
302409
'#758cad',
303410
'#868e67',
304411
'#335862',
305-
'#b6978c',
306412
'#335862',
307-
'#9e8f6e',
308413
'#335862',
309414
'#ac7e8b',
310415
'#77a185',
@@ -314,7 +419,6 @@ public static function getColorMap() {
314419
'#467a70',
315420
'#9b7d73',
316421
'#335862',
317-
'#8a7c5b',
318422
'#335862',
319423
'#8c9c85',
320424
'#335862',
@@ -335,12 +439,4 @@ public static function getColorMap() {
335439
return $map;
336440
}
337441

338-
public static function getBorderMap() {
339-
$map = array(
340-
'rgba(0,0,0,.3);', // Darker
341-
'rgba(255,255,255,.5);', // Lighter
342-
);
343-
return $map;
344-
}
345-
346442
}

0 commit comments

Comments
 (0)