<?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=' ', $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
?>