3 final class PhabricatorFilesComposeAvatarBuiltinFile
4 extends PhabricatorFilesBuiltinFile
{
10 private $maps = array();
14 public function updateUser(PhabricatorUser
$user) {
15 $username = $user->getUsername();
17 $image_map = $this->getMap('image');
18 $initial = phutil_utf8_strtoupper(substr($username, 0, 1));
19 $pack = $this->pickMap('pack', $username);
20 $icon = "alphanumeric/{$pack}/{$initial}.png";
21 if (!isset($image_map[$icon])) {
22 $icon = "alphanumeric/{$pack}/_default.png";
25 $border = $this->pickMap('border', $username);
26 $color = $this->pickMap('color', $username);
28 $data = $this->composeImage($color, $icon, $border);
29 $name = $this->getImageDisplayName($color, $icon, $border);
31 $unguarded = AphrontWriteGuard
::beginScopedUnguardedWrites();
33 $file = PhabricatorFile
::newFromFileData(
42 ->setDefaultProfileImagePHID($file->getPHID())
43 ->setDefaultProfileImageVersion(self
::VERSION
)
51 private function getMap($map_key) {
52 if (!isset($this->maps
[$map_key])) {
55 $map = $this->newPackMap();
58 $map = $this->newImageMap();
61 $map = $this->newColorMap();
64 $map = $this->newBorderMap();
67 throw new Exception(pht('Unknown map "%s".', $map_key));
69 $this->maps
[$map_key] = $map;
72 return $this->maps
[$map_key];
75 private function pickMap($map_key, $username) {
76 $map = $this->getMap($map_key);
77 $seed = $username.'_'.$map_key;
78 $key = PhabricatorHash
::digestToRange($seed, 0, count($map) - 1);
83 public function setIcon($icon) {
88 public function getIcon() {
92 public function setColor($color) {
93 $this->color
= $color;
97 public function getColor() {
101 public function setBorder($border) {
102 $this->border
= $border;
106 public function getBorder() {
107 return $this->border
;
110 public function getBuiltinFileKey() {
111 $icon = $this->getIcon();
112 $color = $this->getColor();
113 $border = implode(',', $this->getBorder());
114 $desc = "compose(icon={$icon}, color={$color}, border={$border}";
115 $hash = PhabricatorHash
::digestToLength($desc, 40);
116 return "builtin:{$hash}";
119 public function getBuiltinDisplayName() {
120 return $this->getImageDisplayName(
126 private function getImageDisplayName($icon, $color, $border) {
127 $border = implode(',', $border);
128 return "{$icon}-{$color}-{$border}.png";
131 public function loadBuiltinFileData() {
132 return $this->composeImage(
138 private function composeImage($color, $image, $border) {
139 // If we don't have the GD extension installed, just return a static
140 // default profile image rather than trying to compose a dynamic one.
141 if (!function_exists('imagecreatefromstring')) {
142 $root = dirname(phutil_get_library_root('phabricator'));
143 $default_path = $root.'/resources/builtin/profile.png';
144 return Filesystem
::readFile($default_path);
147 $color_const = hexdec(trim($color, '#'));
148 $true_border = self
::rgba2gd($border);
149 $image_map = $this->getMap('image');
150 $data = Filesystem
::readFile($image_map[$image]);
152 $img = imagecreatefromstring($data);
154 // 4 pixel border at 50x50, 32 pixel border at 400x400
155 $canvas = imagecreatetruecolor(400, 400);
157 $image_fill = imagefill($canvas, 0, 0, $color_const);
160 pht('Failed to save builtin avatar image data (imagefill).'));
163 $border_thickness = imagesetthickness($canvas, 64);
164 if (!$border_thickness) {
166 pht('Failed to save builtin avatar image data (imagesetthickness).'));
169 $image_rectangle = imagerectangle($canvas, 0, 0, 400, 400, $true_border);
170 if (!$image_rectangle) {
172 pht('Failed to save builtin avatar image data (imagerectangle).'));
175 $image_copy = imagecopy($canvas, $img, 0, 0, 0, 0, 400, 400);
178 pht('Failed to save builtin avatar image data (imagecopy).'));
181 return PhabricatorImageTransformer
::saveImageDataInAnyFormat(
186 private static function rgba2gd($rgba) {
192 return ($a << 24) |
($r << 16) |
($g << 8) |
$b;
195 private function newImageMap() {
196 $root = dirname(phutil_get_library_root('phabricator'));
197 $root = $root.'/resources/builtin/alphanumeric/';
200 $list = id(new FileFinder($root))
202 ->withFollowSymlinks(true)
205 foreach ($list as $file) {
206 $map['alphanumeric/'.$file] = $root.$file;
212 private function newPackMap() {
213 $root = dirname(phutil_get_library_root('phabricator'));
214 $root = $root.'/resources/builtin/alphanumeric/';
216 $map = id(new FileFinder($root))
218 ->withFollowSymlinks(false)
220 $map = array_values($map);
225 private function newBorderMap() {
229 array(255, 255, 255, 0.4),
230 array(255, 255, 255, 0.7),
234 private function newColorMap() {
235 // Via: http://tools.medialab.sciences-po.fr/iwanthue/