[Web] Cravatar

Discussion in 'Bukkit Tools' started by McLive, Sep 21, 2013.

Thread Status:
Not open for further replies.
  1. Offline

    McLive

    Hello,

    today I want to introduce you to our new project: Cravatar.eu.

    What is Cravatar?

    Cravatar is most like Minotar, but has some cool enhancements.
    With Cravatar, you can embed player avatars on your website. Furthermore you have the choice between a “flat” or a “perspective”


    Cravatar is optimized for fast image loading. It wont “slow down” your website.
    You can even change Xenforo plugins to this URL. Pm me for help with this.


    Examples:

    [​IMG][​IMG]
    [​IMG][​IMG]
    [​IMG][​IMG]
    [​IMG][​IMG]

    Check it out on http://cravatar.eu.

    We would like to see your feedback and suggestions.
     
    Last edited: Dec 21, 2015
    rbrick, MrsMeowys, Wruczek and 2 others like this.
  2. Offline

    TNTUP

    I would like to know the code that does perspective view. My webserver does avatars (Minotar fork) and idk if its possible to have the source, I could integrate it inside. Good work though
     
  3. Offline

    turt2live

    Source code and license? I have a few projects I'd like to use this for, and I'd rather not murder your server :p
     
    gomeow likes this.
  4. Offline

    rguz10

    Very nice.
     
  5. Offline

    clone1018

    Really cool, is it forked from Minotar at all? How do you plan on keeping up with the traffic :p?
     
    obscurehero likes this.
  6. Offline

    McLive

    Its not open source, but you can use the service as you want to.

    Cravatar is completely self written and traffic won't be a problem. The server has 20 TB traffic per month.
     
  7. Offline

    turt2live

    That sucks :(
    I'll stick to an open source one then.
     
  8. Offline

    clone1018

    Not the bandwidth, the actual per second request rate. If you're ever interested I can sent 10% of Minotar's traffic your way for a nice test, just let me know.
     
    JOPHESTUS and DrAgonmoray like this.
  9. Offline

    McLive

    Sure, would be a nice test. But what are 10% in real numbers? How many requests per second and different nicks?
     
  10. Offline

    user_43347

    How exactly are the images optimized? Your site doesn't even use a CDN let alone a subdomain to serve static content (btw: your Javascript isn't at the end of the body, so your page loading can hang if the server doesn't respond ASAP).

    I would imagine you atleast cache the images, but even then, your site wouldn't be able to handle a large amount of requests like Minotar. Also, I don't understand how you prevent "slowing down websites". If your site doesn't respond ASAP, the browser will hang regardless to download your image.
     
  11. Offline

    clone1018

    To be fair, Minotar doesn't use a CDN or a specific subdomain to serve our static content, we leverage CloudFlare but that's not a true CDN.
    Thankfully image loading is a secondary thing when it comes to browsers showing them, they don't hang the pages until the images are loading and images are loaded as-requested.


    McLive: 10% is about 500-600rps with an infinite amount of combinations for usernames, sizes and extension combinations.
     
  12. Offline

    TNTUP


    Ah wow. I was looking for the perspective not the full source. I guess I need to look myself for that "view" in my PHP query server that shows heads (player list online on a server)
     
  13. Offline

    JOPHESTUS

    Add one that shows the helmet. My face looks weird without it
    [​IMG]
     
    Datdenkikniet likes this.
  14. Offline

    McLive

  15. Offline

    Hoolean

    Awesome :)
     
  16. Offline

    VpDefault

    This is awesome, will try to integrate it onto the website now. Thanks alot
     
  17. Offline

    McLive

    Cool. Let me know if you have any problems. :)
     
  18. Offline

    Aidoboy

    Is it possible for you to make it do the whole body, or the body minus the legs? It would also be nice if you could get it facing other directions.

    Besides that, really awesome! I'll be using it on http://www.theundarkpixel.com/ !
     
  19. Offline

    McLive

    Yeah, thats planned but will take a while until it's done.
     
  20. Offline

    jtjj222

    Any plans on adding perspective projection? Also, if I may ask, why do you not want to share the source?
     
    lycano likes this.
  21. Offline

    HeroCC

    Love it! Works great.

    [​IMG]
     
  22. Offline

    TNTUP

    Uhm sorry for bugging ye again, I know you said no about the code that does perspective view, I googled hard on the web to find a similiar one but they fail.. I want to integrate it in my Query a Minecraft Server, I dont rely on 3rd party services...

    Someone else suggested me to add it but I can't find the source of teh head generation. Thanks in advance. PS: I looked in your github, no matches.
     
  23. Offline

    Mister_Fix

    please please please source D:
     
    Zarko and TNTUP like this.
  24. Offline

    Adriani6

  25. Offline

    Mister_Fix

    Your face looks weird with the helmet 0_0
     
  26. Offline

    JOPHESTUS

    trew
     
  27. Offline

    tyzoid

    TNTUP turt2live Mister_Fix
    I've implemented something like this in PHP and this was my result. I'll see if I can't speed things up and remove some of the artifacts, but this is the result of an hour of hacking around.:
    Mine:
    skin.png
    Cravatar:
    [​IMG]

    Aand the code (Build off of an old/hacked version of AwesomeSkins)
    skin.php (open)

    PHP:
    <?php
    /*
                                                    _____ _    _                    __  ___
        /\                                        / ____| |  (_)                  /_ | |__ \
        /  \__      _____  ___  ___  _ __ ___  ___| (___ | | ___ _ __  ___    __  _| |  ) |
      / /\ \ \ /\ / / _ \/ __|/ _ \| '_ ` _ \ / _ \\___ \| |/ / | '_ \/ __|    \ \ / / |  / /
      / ____ \ V  V /  __/\__ \ (_) | | | | | |  __/____) |  <| | | | \__ \    \ V /| |_ / /_
    /_/    \_\_/\_/ \___||___/\___/|_| |_| |_|\___|_____/|_|\_\_|_| |_|___/      \_/ |_(_)____|
                                                                                   
                                                                                   
    */
     
    include("Image.php");
     
    $username = isset($_GET['user'])? $_GET['user'] : "Notch";
    $new = isset($_GET['new']);
    $hat = isset($_GET['hat']);
    $box = isset($_GET['box']);
     
    function 
    verifyHat($rawskin){
        
    $c imagecolorat($rawskin408);
        for(
    $i 40$i 48; ++$i){
            for(
    $j 8$j 16; ++$j){
                if(
    $c !== imagecolorat($rawskin1015)) return true;
            }
        }
        return 
    false;
    }
     
    /*echo "username: "; var_dump($username);
    echo "<br />new: "; var_dump($new);
    echo "<br />Hat: "; var_dump($hat);
    echo "<br />GET: "; var_dump($_GET);
    die();*/
     
    if(isset($_GET['update'])){
        
    header("Cache-Control: no-cache, must-revalidate"); // HTTP/1.1
        
    header("Expires: Sat, 26 Jul 1997 05:00:00 GMT"); // Date in the past
    } else {
        
    header("Cache-Control: max-age=3600, public");
    }
     
    if(!(
    strlen($username) > && strlen($username) < 17 && preg_match("/^[A-Za-z0-9_]+$/"$username))){
        die(
    "Script error 100: Invalid Username");
    }
     
    if(
    $new && file_exists("finalskincache/new_$username.png") && !isset($_GET['update']) && !$hat && !$box){
        
    $finalimage imagecreatefrompng("finalskincache/new_$username.png");
        
    header("Content-Type: image/png");
        
    imagepng($finalimage);
        die();
    } else if(!
    $new && file_exists("finalskincache/$username.png") && !isset($_GET['update']) && !$hat && !$box){
        
    $finalimage imagecreatefrompng("finalskincache/$username.png");
        
    header("Content-Type: image/png");
        
    imagepng($finalimage);
        die();
    } else if(
    file_exists ("skincache/$username.png") && !isset($_GET['update'])){
        
    $original_skin imagecreatefrompng("skincache/$username.png");
    } else {
        
    set_time_limit(5);
        
    $original_skin = @imagecreatefrompng("http://s3.amazonaws.com/MinecraftSkins/$username.png");
        
    set_time_limit(25);
        if(!
    $original_skin) die("Script error 101: Access denied or skin doesn't exist");
        
    imagealphablending($original_skinfalse);
        
    imagesavealpha($original_skintrue);
        
    imagepng($original_skin"skincache/$username.png");
    }
     
    $finalimage imagecreatetruecolor(100170);
     
    $ablack imagecolorallocate($finalimage100);
    $black imagecolorallocate($finalimage000);
    imagefill($finalimage00$ablack);
    imagecolortransparent($finalimage$ablack);
     
     
     
    if (
    $box){
        
    $size  200;
        
    $seach .5*$size;
     
        
    $parts = array();
        
    $parts["head"] = imagecreatetruecolor($size$size);
        
    $parts["top"]  = imagecreatetruecolor($size*2$size*2);
        
    $parts["side"] = imagecreatetruecolor($size$size);
     
        foreach(
    $parts as &$img){
            
    imagefill($img000x7F000000);
        }
     
        
    imagecopyresized($parts["head"], $original_skin0, ($size-$seach), 88$seach$seach88);
        
    imagecopyresized($parts["top"],  $original_skin0, ($size-$seach), 80$seach$seach88);
        
    imagecopyresized($parts["side"], $original_skin0, ($size-1.4*$seach), 08$seach$seach88);
     
        if(
    $hat && verifyHat($original_skin)){
            
    imagecopyresized($parts["head"], $original_skin0, ($size-$seach), 408$seach$seach88);
            
    imagecopyresized($parts["side"], $original_skin0, ($size-1.4*$seach), 328$seach$seach88);
        }
     
        
    // Face
        
    $m = array(
            array(
    10),
            array(-
    .41)
        );
     
        
    $image = new Image(imagesx($parts["head"]), imagesy($parts["head"]), $parts["head"]);
        if(
    $image->affine($m) !== true) echo "";// die("Transformation Error");
        
    $parts["head"] = $image->getImage();
     
        
    // Side
        
    $m = array(
            array(
    .60),
            array(
    .41)
        );
     
        
    $image = new Image(imagesx($parts["side"]), imagesy($parts["side"]), $parts["side"]);
        if(
    $image->affine($m) !== true) die("Transformation Error");
        
    $parts["side"] = $image->getImage();
     
        
    // Top
        
    $m = array(
            array(
    1.6),
            array(-
    .4.4)
        );
    /*    $m = array(
            array(1, 0),
            array(0, 1)
        );
    /**/
        
    $image = new Image(imagesx($parts["top"]), imagesy($parts["top"]), $parts["top"]);
        if(
    $image->affine($m) !== true) die("Transformation Error");
        
    $parts["top"] = $image->getImage();
     
        
    $fim imagecreatetruecolor($size$size);
        
    imagefill($fim000x7F000000);
     
        
    imagealphablending($fimtrue);
        
    imagesavealpha($fimtrue);
        
    imagecopy($fim$parts["top"], 00, ($size-1.4*$seach), 0$size$size);
        
    imagecopy($fim$parts["head"], ($size-1.4*$seach), 00$size*.101$size$size*.899);
        
    imagecopy($fim$parts["side"], 000$size*.101$size$size*.899);
     
        
    header("Content-Type: image/png");
     
        
    imagepng($fim);
    } else {
        
    imagefilledrectangle($finalimage00100100$black);
        
    imagefilledrectangle($finalimage1010090140$black);
        
    imagefilledrectangle($finalimage3014070170$black);
        
    imagecopyresized($finalimage$original_skin101088808088);            //head
     
        
    if($new){
            
    imagecopyresized($finalimage$original_skin2010040201030412);    //arm 1
            
    imagecopyresized($finalimage$original_skin7010040201030412);    //arm 2
            
    imagecopyresized($finalimage$original_skin4010020202030812);    //body
     
            
    imagecopyresized($finalimage$original_skin40150816101044);    //foot 1
            
    imagecopyresized($finalimage$original_skin50150816101044);    //foot 2
            
    if($hat && verifyHat($original_skin)){
                
    imagecopyresized($finalimage$original_skin1010408808088);    //hat
            
    } else {
                
    imagepng($finalimage"finalskincache/new_$username.png");
            }
        } else {
            
    imagecopyresized($finalimage$original_skin201104030102012);    //arm 1
            
    imagecopyresized($finalimage$original_skin701104030102012);    //arm 2
            
    imagecopyresized($finalimage$original_skin401002321203023);    //body
     
            
    imagecopyresized($finalimage$original_skin40150816101011);    //foot 1
            
    imagecopyresized($finalimage$original_skin50150816101011);    //foot 2
            
    if($hat && verifyHat($original_skin)){
                
    imagecopyresized($finalimage$original_skin1010408808088);    //hat
            
    } else {
                
    imagepng($finalimage"finalskincache/$username.png");
            }
        }
    }
     
    header("Content-Type: image/png");
     
    imagepng($finalimage);
    imagedestroy($finalimage);
    Image.php (open)

    PHP:
    <?php
    class Image{
        private 
    $image = array(array());
        private 
    $w 0;
        private 
    $h 0;
        private 
    $color 0x7FFFFFFF//Fully transparent white
     
        
    public function __construct($width$height$image null){
            
    //Image attributes
            
    $this->$width;
            
    $this->$height;
     
            for(
    $i=0$i<$width$i++){
                for(
    $j=0$j<$height$j++){
                    if(
    $image === null$this->image[$i][$j] = $this->color;
                    else 
    $this->image[$i][$j] = imagecolorat($image$i$j);
                }
            }
        }
     
        public function 
    setBgColor($color){
            
    $this->color $color 0x7FFFFFFF;
        }
     
        public function 
    affine($m){
            
    //Invert matrix given
            
    $det $m[0][0]*$m[1][1]-$m[0][1]*$m[1][0];
            if(
    $det == 0) return false;
     
            
    $inv = array(
                    array(
    $m[1][1]/$det, -$m[0][1]/$det),
                    array(-
    $m[1][0]/$det$m[0][0]/$det)
                );
     
     
            
    $nim = array(array());
     
            foreach(
    $this->image as $y => $row){
                foreach(
    $row as $x => $pixel){
                    
    $tf $this->mm($inv, array($x$y));
                    
    $nim[$x][$y] = $this->getApproximation($tf[0], $tf[1]);
                }
            }
     
            
    $this->image $nim;
     
            return 
    true;
        }
     
        public function 
    getImage(){
            
    //Convert back to image
            
    $fim imagecreatetruecolor($this->w,$this->h);
            
    imagefill($fim00$this->color);
            
    imagealphablending($fimfalse);
            
    imagesavealpha($fimtrue);
            foreach(
    $this->image as $x=>$row){
                foreach(
    $row as $y=>$pixel){
                    
    imagesetpixel($fim$x$y$pixel);
                }
            }
     
            return 
    $fim;
        }
     
        private function 
    getApproximation($x$y){
            if(
    $x <= -|| $x >= $this->w) return $this->color;
            if(
    $y <= -|| $y >= $this->h) return $this->color;
     
            
    $pa = (floor($x)<|| floor($y)<0)? $this->color $this->image[floor($x)][floor($y)];
            
    $pb = (floor($x)<|| ceil($y)>=$this->h)? $this->color $this->image[floor($x)][ceil($y)];
            
    $pc = (ceil($x)>=$this->|| floor($y)<0)? $this->color $this->image[ceil($x)][floor($y)];
            
    $pd = (ceil($x)>=$this->|| ceil($y)>=$this->h)? $this->color $this->image[ceil($x)][ceil($y)];
     
            
    $dx $x-floor($x);
            
    $dy $y-floor($y);
     
            
    $a round((($pa>>24)&0x7F)*(1-$dx)*(1-$dy) + (($pb>>24)&0x7F)*(1-$dx)*($dy) + (($pc>>24)&0x7F)*($dx)*(1-$dy) + (($pd>>24)&0x7F)*($dx)*($dy));
            
    $r round((($pa>>16)&0xFF)*(1-$dx)*(1-$dy) + (($pb>>16)&0xFF)*(1-$dx)*($dy) + (($pc>>16)&0xFF)*($dx)*(1-$dy) + (($pd>>16)&0xFF)*($dx)*($dy));
            
    $g round((($pa>>8)&0xFF)*(1-$dx)*(1-$dy) +  (($pb>>8)&0xFF)*(1-$dx)*($dy) +  (($pc>>8)&0xFF)*($dx)*(1-$dy) +  (($pd>>8)&0xFF)*($dx)*($dy));
            
    $b round(($pa&0xFF)*(1-$dx)*(1-$dy) +      ($pb&0xFF)*(1-$dx)*($dy) +      ($pc&0xFF)*($dx)*(1-$dy) +      ($pd&0xFF)*($dx)*($dy));
     
            return ((
    $a<<24) + ($r<<16) + ($g<<8) + $b);
        }
     
        private function 
    mm($m$v){
            return array(
    $m[0][0]*$v[0]+$m[0][1]*$v[1], $m[1][0]*$v[0]+$m[1][1]*$v[1]);
        }
    }


    Edit: Again, this code is slow for larger images (took 5s to render a 500x500 version) so I'll work on optimizing it, but this should help with some of the perspective stuff you were talking about.

    Edit 2: As usual, code licensed under the TPL: https://raw.github.com/tyzoid/Colors/master/readme.md

    Edit 3: Let's try and keep this thread on-topic, so if you have any questions about my code, feel free to PM me.
     
  28. Offline

    Mister_Fix

    Well, thank you for actually trying. my suggestion is to keep the image limit to 300PX, that's all, if i manage to fix your code i will post it here.
     
  29. Offline

    MrInspector

    I love this tool, thanks for making it! :D
     
  30. Offline

    rustehiscool

    Awesome good job! :D
     
Thread Status:
Not open for further replies.

Share This Page