Location: PHPKode > scripts > FineDiv > finediv/class_finediv.php
<?php
/*
* Filename.....: class_finediv.php
* Class........: finediv
* Purpose......: Implement "liquid corners" as seen at
*                http://home.tiscali.nl/developerscorner/liquidcorners/liquidcorners.htm
*                php creates the image needed effortless "on the fly".
*                php creates also the css file and links it to the web page.
*
* Original.....: Liquid round corners
* Title          Adaptable rounded edges
*                a multifunctional flexible css-concept with transparency
*                for creating dynamically changing not rectangular borders
*
* Idea.........: by francky kleyneman (sept./oct. 2005)
*
* Erstellt am..: Dienstag, 18. April 2006
*       _  __      _ _
*  ||| | |/ /     (_) |        Wirtschaftsinformatiker IHK
* \. ./| ' / _ __  _| |_ ___   www.ingoknito.de
* - ^ -|  < | '_ \| | __/ _ \
* / - \| . \| | | | | || (_) | Peter Klauer
*  ||| |_|\_\_| |_|_|\__\___/  06136-909093
* mailto.......: hide@address.com
*
*/

class finediv
{

  /**
  * All colour values must be given using six (6) hex chars.
  */

  var $color   = 'EFEFEF'; // enter colors as 6 letters without leading # or $
  var $bgcolor = 'FFFFFF'; // finediv will always try to make the bgcolor transparent
  var $bordercolor = 'C00000';
  var $textcolor = '000000'; // default colour for box text

  var $style = ''; // addional style for text in the middle
  var $topstyle = ''; // additional style for Text in notopgap
  var $bottomstyle = ''; // addional style for text in the nobottomgap

  var $radius = 15; // default corner width
  var $cssdir = './cssdir/'; // Path to cssdir folder for corner cssdir
  var $borderwidth = 0; // Width of border in pixel

  #
  # not ready yet: Other types than rounded corner
  #
  var $type = 1; // 1 = rounded, [not yet: 2 = cut, 3 = negative rounded, 4 = nest ]

  #
  # Tweaking section
  #
  var $factor = 5; // Play here to get different resampling results
  var $numpalettecolors = 7; // Play here to experience strange corner shadowing effects
  var $format = 'gif'; // gif, jpeg or jpg, png, bmp or wbmp
  var $quality= 85; // quality for jpeg cssdir
  var $newline = "\n"; // unix-type newline for <link>
  var $maxwidth = 4096; // Making such a wide image by hand is not a pleasant task.



  #
  # internals
  #

  var $name = ''; // for the css-class. Has a preceding "f" when $imagename starts with number
  var $imagename = ''; // The md5 imagename is used to decide whether to create everything or not


  /**
  * The constructor takes the path to the css directory.
  * The image for the box is also put into this directory.
  * So the css file may be accessed easily from any point within the directory structure.
  * (or outside...)
  *
  * @param string $cssdir = Path to directory
  */
  function finediv( $cssdir = './css/' )
  {
    $this->cssdir = $cssdir;

    if( ! is_dir( $cssdir ) )
    {
      die( "<br>FINEDIV: [$cssdir] is not a directory!" );
    }

  }


  /**
  * Generates the basic image and css file for this class.
  * Links the stylesheet, so this is to be called in the <head> section.
  * Does not generate anything if the image already exists.
  * @param string $name = Name of the box and the css classes
  */
  function install()
  {

    #
    # a) Check if the cssdir are already in the cssdir folder
    # b) if not, then generate an image using $color, $bgcolor and $radius.
    #    Just draw a circle and save it to $this->cssdir
    # c) Split this image into four pieces and save them into the image folder
    #

    # Generate an md5-hash-name for the big image.
    # The big image is never shown. But it is the base for all corner cssdir.
    # Instead of testing every corner we only test the big one.

    # just to be sure: add a slash / at the end of $this->cssdir if missing
    if( ! (substr( $this->cssdir, strlen($this->cssdir )-1, 1) == '/' ) ) $this->cssdir .= '/';

    #
    # ok ok, the resulting file name is ugly. I understand.
    # But it is UNIQUE and that is the point. You may have several differently
    # coloured boxes on your page. Humans do not need to bother about names,
    # since the class will remember everything what is necessary.
    #
    $this->imagename =
    md5( $this->radius.
    $this->color.
    $this->bgcolor.
    $this->borderwidth.
    $this->bordercolor.
    $this->textcolor.
    $this->topstyle.
    $this->style.
    $this->bottomstyle.
    $this->factor.
    $this->numpalettecolors.
    $this->maxwidth.
    $this->quality.
    $this->type );

    echo $this->newline.'<link rel="stylesheet" type="text/css" href="'.
    $this->cssdir.$this->imagename.'.css">';

    $c = $this->is_alpha( substr( $this->imagename, 0, 1 ) ) ? '' : 'f';

    $this->name = $c.$this->imagename; // an alpha char as first char for css2 when numeric. "f" for "finediv"

    #
    # All the rest of the install() function is only executed when
    # the image and css-file have to be generated because they do not exist yet.
    # after creating the cssdir, the file_exists() will find the big one
    # and the install() function will exit immediately after checking
    #

    if( ! file_exists( $this->cssdir.$this->imagename.'.'.$this->format ) )
    {


      # Go and create a big circle with fill color $color and background $bgcolor
      # since we only use 2 or three colors, palette based image creation is enough

      $diameter = $this->radius*2+1;

      # another attempt:
      # generate circle with border twice as large as needed
      # then resample it.

      # a resample factor > 200 is unsane for the server!
      # The factor is responsible for different results.
      # The larger the factor is, the larger the temporary image for resampling.
      # A very large factor does not mean a very good result!
      # You may test it with "100" to see a cut edge.
      if( $this->factor > 200 ) $this->factor = 200;
      if( $this->factor < 1   ) $this->factor = 1;

      if( $this->radius < 1 ) die( 'FINEDIV::INSTALL(): radius must be greater than zero.' );


      $newr = $this->radius * $this->factor;
      $newd = $newr * 2 + 1;

      # gd_info() returns array
      $truecolor = function_exists( 'gd_info' ) ? true : false;
      if( !$truecolor ){ die('FINEDIV::INSTALL(): Sorry, GD too old...'); }



      $imbig = imagecreatetruecolor( $newd, $newd );

      list( $cred, $cgreen, $cblue ) = $this->rgb( $this->color );
      list( $bred, $bgreen, $bblue ) = $this->rgb( $this->bgcolor );
      $bgcolor = imagecolorallocatealpha( $imbig, $bred, $bgreen, $bblue, 127 ); // for png & co

      imagecolortransparent( $imbig, $bgcolor ); // for gif
      $color   = imagecolorallocate( $imbig, $cred, $cgreen, $cblue ); // for all
      imagefill( $imbig, 0, 0, $bgcolor );

      # draw filled circle with bordercolor
      if( $this->borderwidth > 0 )
      {
        
        list( $bcred, $bcgreen, $bcblue ) = $this->rgb( $this->bordercolor );
        $bordercolor = imagecolorallocate($imbig, $bcred, $bcgreen, $bcblue);
        imagefilledellipse( $imbig, $newr, $newr, $newd, $newd, $bordercolor );

      }

      # the width of the filled circle is normal width * $this->factor
      $width = $newd - ($this->borderwidth*2*$this->factor);

      imagefilledellipse( $imbig, $newr, $newr, $width, $width, $color );

      $im = imagecreatetruecolor( $diameter, $diameter );

      list( $bcred, $bcgreen, $bcblue ) = $this->rgb( $this->bordercolor );
      list( $cred, $cgreen, $cblue ) = $this->rgb( $this->color );
      list( $bred, $bgreen, $bblue ) = $this->rgb( $this->bgcolor );

      $bgcolor = imagecolorallocatealpha( $im, $bred, $bgreen, $bblue, 127 ); // for png & co
      imagecolortransparent( $im, $bgcolor ); // for gif
      $color   = imagecolorallocate( $im, $cred, $cgreen, $cblue ); // for all
      $bordercolor = imagecolorallocate($im, $bcred, $bcgreen, $bcblue);


      # fill entire image with the background color $bgcolor
      imagefill( $im, 0, 0, $bgcolor );

      imagecopyresampled($im,$imbig,0,0,0,0,$diameter,$diameter,$newd,$newd);
      imagedestroy( $imbig );

      imagetruecolortopalette ( $im, false, $this->numpalettecolors );


      imagesavealpha($im, false ); // only single color transparence needed


      $imbig = imagecreate( $this->maxwidth, $diameter );

      $bigbgcolor = imagecolorallocatealpha( $imbig, $bred, $bgreen, $bblue, 127 ); // for png & co
      imagecolortransparent( $imbig, $bigbgcolor ); // for gif
      $bigcolor   = imagecolorallocate( $imbig, $cred, $cgreen, $cblue ); // for all
      imagesavealpha($imbig, false ); // only single color transparence needed


      if( $this->borderwidth > 0 )
      {
        $bigbordercolor = imagecolorallocate($imbig, $bcred, $bcgreen, $bcblue);
        imagefilledrectangle($imbig, 0,0, $this->maxwidth,$diameter,$bigbordercolor);

        $height = $diameter - $this->borderwidth -1;
        $width  = $this->maxwidth - $this->borderwidth*2;
      }
      else
      {
        $height = $diameter;
        $width = $this->maxwidth;
      }

      # fill the middle, the border stays
      imagefilledrectangle($imbig, $this->borderwidth, $this->borderwidth, $width,$height, $bigcolor);

      # clear both ends for transparent bg
      imagefilledrectangle($imbig, 0, 0, $this->radius-1, $diameter, $bigbgcolor );
      imagefilledrectangle($imbig, $this->maxwidth-$this->radius, 0, $this->maxwidth, $diameter, $bigbgcolor );

      # copy the left curves with transparent bg
      imagecopy( $imbig, $im, 0, 0, 0, 0, $this->radius, $diameter );
      # copy the right curves
      imagecopy( $imbig, $im, $this->maxwidth-$this->radius,0,$this->radius+1,0,$this->radius,$diameter);

      imagedestroy( $im ); // delete circle, no longer needed

      # image is ready. But 1 pixel too high for the liquid corners solution to
      # work as described (lower border of image will hide 1 pixel)
      # new task: Delete the middle pixel row
      # a) copy lower half up 1 pixel
      imagecopy( $imbig, $imbig, 0, $this->radius, 0, $this->radius+1, $this->maxwidth, $this->radius );
      # b) delete the last pixel row
      #    create a new image which is 1 pixel smaller in height
      #    make the colours and copy the picture

      $imbig2 = imagecreate( $this->maxwidth, $this->radius*2 );

      #
      # copy everything except the last row
      #
      imagecopy( $imbig2, $imbig, 0,0,0,0,$this->maxwidth,$this->radius*2 );


      $bigbgcolor = imagecolorallocatealpha( $imbig2, $bred, $bgreen, $bblue, 127 ); // for png & co
      imagecolortransparent( $imbig2, $bigbgcolor ); // for gif
      $bigcolor   = imagecolorallocate( $imbig2, $cred, $cgreen, $cblue ); 
      $bigcolor   = imagecolorclosest( $imbig2, $cred, $cgreen, $cblue ); // necessary

      imagesavealpha($imbig2, false ); // only single color transparence needed

      if( $this->type == 1 ) // for rounded corners
      {

        # truecolor kills gif tranparency. Remake the corners.
        imagefill( $imbig2, 0, 0, $bigbgcolor );
        imagefill( $imbig2, $this->maxwidth-1, 0, $bigbgcolor );
        imagefill( $imbig2, 0, $diameter-2, $bigbgcolor );
        imagefill( $imbig2, $this->maxwidth-1, $diameter-2, $bigbgcolor );

        # we filled the outside of each corner with transparency.
        # but the inside of some corners may be filled with the wrong color
        # when beeing resampled from truecolor
        # the inside of the corner has to be filled, too

        imagefill( $imbig2, $this->radius-1, $this->radius-1, $bigcolor );
        imagefill( $imbig2, $this->maxwidth-$this->radius+1,$this->radius-1, $bigcolor );

      }
      
      imagedestroy( $imbig ); // no longer needed

      switch( $this->format )
      {
        case 'gif': imagegif( $imbig2, $this->cssdir.$this->imagename.'.'.$this->format ); break;
        case 'jpg':
        case 'jpeg': imagejpeg($imbig2, $this->cssdir.$this->imagename.'.'.$this->format ); break;
        case 'png': imagepng($imbig2, $this->cssdir.$this->imagename.'.'.$this->format ); break;
        case 'bmp':
        case 'wbmp': imagewbmp($imbig2, $this->cssdir.$this->imagename.'.'.$this->format ); break;
        default:
        die( "<br>FINEDIV::INSTALL(): No correct format $this->format specified! (gif,jpg,jpeg,png,bmp,wbmp)" );
      }

      imagedestroy( $imbig2 );

      #
      # now generate the css template
      #
      # a) read the template which resides in the cssdir folder
      # b) replace values in [] and
      # c) save it with new [name] into the cssdir
      #

      # a) read the css template
      $file = $this->cssdir.'liquidcorners.inc';
      if( ! file_exists( $file ) )
      {
        die("<br>FINEDIV::INSTALL(): File [$file] is missing in the [$this->cssdir] directory.");
      }

      /**
      * file_get_contents() is not implemented on every webspace
      * simulating the function.
      */
      $zeilen = file( $file );
      $css = '';
      while( list( $k, $v ) = each( $zeilen ) ) $css .= $v;

      # b) replace the keywords with the values, renaming the styles
      $css = str_replace( '[radius]', $this->radius, $css );
      $css = str_replace( '[name]', $this->name, $css );
      $css = str_replace( '[image]', $this->imagename.'.'.$this->format, $css );
      $css = str_replace( '[cssfile]', $this->cssdir.$this->imagename.'.css', $css );
      $css = str_replace( '[date]', date('Y-m-d H:i'), $css );
      if( $this->borderwidth < 1 )
      {
        $border = 'none';
      }
      else
      {
        $border = $this->borderwidth."px solid #$this->bordercolor";
      }
      $css = str_replace( '[border]', $border, $css );
      $css = str_replace( '[color]', $this->color, $css );
      $css = str_replace( '[textcolor]', $this->textcolor, $css );

      $css = str_replace( '[topstyle]', $this->topstyle, $css );
      $css = str_replace( '[bottomstyle]', $this->bottomstyle, $css );
      $css = str_replace( '[style]', $this->style, $css );

      # c) save the template with a unique name

      $handle = fopen( $this->cssdir.$this->imagename.'.css', 'w' );
      fwrite( $handle, $css, strlen( $css ) );
      fclose( $handle );


    } // if ! file_exists()



  } // eof install()


  /**
  * return an array of values (red, green, blue) from a hex color value
  * @param string $hexvalue
  * @return array
  */
  function rgb( $hexvalue )
  {

    if( strlen( $hexvalue ) <> 6 ) die( "<br>FINEDIV::RGB() - not a 6 char hex value [$hexvalue]");
    $c = str_replace( '#', '', $hexvalue );
    $c = str_replace( '$', '', $c );
    if( strlen( $c ) <> 6 ) die( "<br>FINEDIV::RGB() - not a 6 char hex value [$hexvalue]");

    $red   = hexdec( substr( $c, 0, 2 ) );
    $green = hexdec( substr( $c, 2, 2 ) );
    $blue  = hexdec( substr( $c, 4, 2 ) );

    return array( $red, $green, $blue );

  } // eof finediv::rgb()


  /**
  * Return the string for the top part of a rounded box
  * @param string $text = Text to be put into the "notopgap" paragraph
  */
  function gettop($text='&nbsp;', $close_notopgap = false )
  {

    return '<div class="'.$this->name.'-top-left"></div><div class="'.
    $this->name.'-top-right"></div><div class="'.
    $this->name.'-inside"><p class="'.$this->name.'-notopgap">'.$text;
    if( $close_notopgap ) echo '</p>';

  }


  /**
  * Output the top part of a rounded box
  * @param string $text = Text to be put into the "notopgap" paragraph
  */
  function puttop($text='')
  {

    echo $this->gettop($text);

  } // eof finediv::puttop()


  function getbottom($text='', $put_nobottomgap = true )
  {

    $r = '';
    if( $put_nobottomgap ) $r.= '<p class="'.$this->name.'-nobottomgap">';
    $r .= $text;
    $r .= '</div><div class="'. // close inside-div
    $this->name.'-bottom-left"></div><div class="'.
    $this->name.'-bottom-right"></div>';

    return $r;
  }

  /**
  * Output the bottom part of a rounded box
  * @param string $text = Text to be put into the "nobottomgap" paragraph
  */
  function putbottom($text='', $put_nobottomgap = true )
  {

    echo $this->getbottom( $text, $put_nobottomgap );

  } // eof finediv::putbottom()


  function get( $toptext = '', $middletext = '', $bottomtext = '' )
  {

    return $this->gettop( $toptext ).
    $middletext.
    $this->getbottom( $bottomtext );

  }


  /**
  * Output the whole box.
  * Has 3 areas where some text may be placed
  * @param string $toptext
  * @param string $middletext
  * @param string $bottomtext
  */
  function put( $toptext = '', $middletext = '', $bottomtext = '' )
  {

    echo $this->get( $toptext, $middletext, $bottomtext );

  } // eof finediv::put()


  /** following two non-optimal functions to output stripes.
  * used to generate multicolored boxes (eg flags)
  */


  /**
  * put an inner stripe.
  * supposes a precedent "puttop()" which opened a $name-inside class div
  * needed for something like "german flag"
  *
  * @param string $text = Text to be put
  * @param boolean $preceding_puttop = true when a puttop() has been issued
  */
  function putinner( $text = '', $preceding_puttop = true )
  {

    if( $preceding_puttop == true ) echo '</div>'; // close it
    echo '<div class="'.$this->name.'-inside"><p class="'.$this->name.'-notopgap">'.$text;

  }


  /**
  * another way of putting an inner stripe.
  *
  * @param string $text = Text to be output
  */
  function stripe( $text = '' )
  {

    echo '<p class="'.$this->name.'-nobottomgap"></p>';
    echo '</div><div class="'.
    $this->name.'-inside"><p class="'.$this->name.'-notopgap">'.$text;


  } // eof finediv::putbottom()


  /**
  * needed to rename the class if the md5 name starts with a number.
  *
  * @param string $char
  * @return boolean
  */
  function is_alpha($char)
  {
    $char = ord($char);
    if($char >= 0x61 && $char <= 0x7a) return 1;
    if($char >= 0x41 && $char <= 0x5a) return 2;
    return 0;
  }



} // eoc finediv

?>
Return current item: FineDiv