picture = imagecreatetruecolor($xSize, $ySize); $C_White = $this->allocateColor(new Color(255, 255, 255)); imagefilledrectangle($this->picture, 0, 0, $xSize, $ySize, $C_White); if($transparent) imagecolortransparent($this->picture, $C_White); $this->antialiasQuality = 0; } function drawFilledRectangle(Point $corner1, Point $corner2, Color $color, ShadowProperties $shadowProperties, $drawBorder = false, $alpha = 100, $lineWidth = 1, $lineDotSize = 0) { if ($corner2->getX() < $corner1->getX()) { $newCorner1 = new Point($corner2->getX(), $corner1->getY()); $newCorner2 = new Point($corner1->getX(), $corner2->getY()); $corner1 = $newCorner1; $corner2 = $newCorner2; } if ($corner2->getY() < $corner1->getY()) { $newCorner1 = new Point($corner1->getX(), $corner2->getY()); $newCorner2 = new Point($corner2->getX(), $corner1->getY()); $corner1 = $newCorner1; $corner2 = $newCorner2; } $X1 = $corner1->getX(); $Y1 = $corner1->getY(); $X2 = $corner2->getX(); $Y2 = $corner2->getY(); if ($alpha == 100) { /* Process shadows */ if ($shadowProperties->active) { $this->drawFilledRectangle(new Point($X1 + $shadowProperties->xDistance, $Y1 + $shadowProperties->yDistance), new Point($X2 + $shadowProperties->xDistance, $Y2 + $shadowProperties->yDistance), $shadowProperties->color, ShadowProperties::NoShadow(), FALSE, $shadowProperties->alpha); if ($shadowProperties->blur != 0) { $AlphaDecay = ($shadowProperties->alpha / $shadowProperties->blur); for($i = 1; $i <= $shadowProperties->blur; $i ++) $this->drawFilledRectangle(new Point($X1 + $shadowProperties->xDistance - $i / 2, $Y1 + $shadowProperties->yDistance - $i / 2), new Point($X2 + $shadowProperties->xDistance - $i / 2, $Y2 + $shadowProperties->yDistance - $i / 2), $shadowProperties->color, ShadowProperties::NoShadow(), FALSE, $shadowProperties->alpha - $AlphaDecay * $i); for($i = 1; $i <= $shadowProperties->blur; $i ++) $this->drawFilledRectangle(new Point($X1 + $shadowProperties->xDistance + $i / 2, $Y1 + $shadowProperties->yDistance + $i / 2), new Point($X2 + $shadowProperties->xDistance + $i / 2, $Y2 + $shadowProperties->xDistance + $i / 2), $shadowProperties->color, ShadowProperties::NoShadow(), FALSE, $shadowProperties->alpha - $AlphaDecay * $i); } } $C_Rectangle = $this->allocateColor($color); imagefilledrectangle($this->picture, round ( $X1 ), round ( $Y1 ), round ( $X2 ), round ( $Y2 ), $C_Rectangle ); } else { $LayerWidth = abs ( $X2 - $X1 ) + 2; $LayerHeight = abs ( $Y2 - $Y1 ) + 2; $this->Layers [0] = imagecreatetruecolor ( $LayerWidth, $LayerHeight ); $C_White = imagecolorallocate( $this->Layers [0], 255, 255, 255); imagefilledrectangle ( $this->Layers [0], 0, 0, $LayerWidth, $LayerHeight, $C_White ); imagecolortransparent ( $this->Layers [0], $C_White ); $C_Rectangle = imagecolorallocate( $this->Layers [0], $color->r, $color->g, $color->b); imagefilledrectangle ( $this->Layers [0], round ( 1 ), round ( 1 ), round ( $LayerWidth - 1 ), round ( $LayerHeight - 1 ), $C_Rectangle ); imagecopymerge ($this->picture, $this->Layers [0], round ( min ( $X1, $X2 ) - 1 ), round ( min ( $Y1, $Y2 ) - 1 ), 0, 0, $LayerWidth, $LayerHeight, $alpha); imagedestroy ( $this->Layers [0] ); } if ($drawBorder) { $this->drawRectangle(new Point($X1, $Y1), new Point($X2, $Y2), $color, $lineWidth, $lineDotSize, ShadowProperties::NoShadow()); } } public function drawRectangle(Point $corner1, Point $corner2, Color $color, $lineWidth, $lineDotSize, ShadowProperties $shadowProperties) { $X1 = $corner1->getX() - .2; $Y1 = $corner1->getY() - .2; $X2 = $corner2->getX() + .2; $Y2 = $corner2->getY() + .2; $this->drawLine(new Point($X1, $Y1), new Point($X2, $Y1), $color, $lineWidth, $lineDotSize, $shadowProperties); $this->drawLine(new Point($X2, $Y1), new Point($X2, $Y2), $color, $lineWidth, $lineDotSize, $shadowProperties); $this->drawLine(new Point($X2, $Y2), new Point($X1, $Y2), $color, $lineWidth, $lineDotSize, $shadowProperties); $this->drawLine(new Point($X1, $Y2), new Point($X1, $Y1), $color, $lineWidth, $lineDotSize, $shadowProperties); } public function drawRoundedRectangle(Point $point1, Point $point2, $radius, Color $color, $lineWidth, $lineDotSize, ShadowProperties $shadowProperties) { $Step = 90 / ((M_PI * $radius) / 2); for($i = 0; $i <= 90; $i = $i + $Step) { $X = cos ( ($i + 180) * M_PI / 180 ) * $radius + $point1->getX() + $radius; $Y = sin ( ($i + 180) * M_PI / 180 ) * $radius + $point1->getY() + $radius; $this->drawAntialiasPixel(new Point($X, $Y), $color, $shadowProperties); $X = cos ( ($i - 90) * M_PI / 180 ) * $radius + $point2->getX() - $radius; $Y = sin ( ($i - 90) * M_PI / 180 ) * $radius + $point1->getY() + $radius; $this->drawAntialiasPixel(new Point($X, $Y), $color, $shadowProperties); $X = cos ( ($i) * M_PI / 180 ) * $radius + $point2->getX() - $radius; $Y = sin ( ($i) * M_PI / 180 ) * $radius + $point2->getY() - $radius; $this->drawAntialiasPixel(new Point($X, $Y), $color, $shadowProperties); $X = cos ( ($i + 90) * M_PI / 180 ) * $radius + $point1->getX() + $radius; $Y = sin ( ($i + 90) * M_PI / 180 ) * $radius + $point2->getY() - $radius; $this->drawAntialiasPixel(new Point($X, $Y), $color, $shadowProperties); } $X1 = $point1->getX() - .2; $Y1 = $point1->getY() - .2; $X2 = $point2->getX() + .2; $Y2 = $point2->getY() + .2; $this->drawLine(new Point($X1 + $radius, $Y1), new Point($X2 - $radius, $Y1), $color, $lineWidth, $lineDotSize, $shadowProperties); $this->drawLine(new Point($X2, $Y1 + $radius), new Point($X2, $Y2 - $radius), $color, $lineWidth, $lineDotSize, $shadowProperties); $this->drawLine(new Point($X2 - $radius, $Y2), new Point($X1 + $radius, $Y2), $color, $lineWidth, $lineDotSize, $shadowProperties); $this->drawLine(new Point($X1, $Y2 - $radius), new Point($X1, $Y1 + $radius), $color, $lineWidth, $lineDotSize, $shadowProperties); } /** * This function creates a filled rectangle with rounded corners * and antialiasing */ function drawFilledRoundedRectangle(Point $point1, Point $point2, $radius, Color $color, $lineWidth, $lineDotSize, ShadowProperties $shadowProperties) { $C_Rectangle = $this->allocateColor($color); $Step = 90 / ((M_PI * $radius) / 2); for($i = 0; $i <= 90; $i = $i + $Step) { $Xi1 = cos ( ($i + 180) * M_PI / 180 ) * $radius + $point1->getX() + $radius; $Yi1 = sin ( ($i + 180) * M_PI / 180 ) * $radius + $point1->getY() + $radius; $Xi2 = cos ( ($i - 90) * M_PI / 180 ) * $radius + $point2->getX() - $radius; $Yi2 = sin ( ($i - 90) * M_PI / 180 ) * $radius + $point1->getY() + $radius; $Xi3 = cos ( ($i) * M_PI / 180 ) * $radius + $point2->getX() - $radius; $Yi3 = sin ( ($i) * M_PI / 180 ) * $radius + $point2->getY() - $radius; $Xi4 = cos ( ($i + 90) * M_PI / 180 ) * $radius + $point1->getX() + $radius; $Yi4 = sin ( ($i + 90) * M_PI / 180 ) * $radius + $point2->getY() - $radius; imageline($this->picture, $Xi1, $Yi1, $point1->getX() + $radius, $Yi1, $C_Rectangle); imageline($this->picture, $point2->getX() - $radius, $Yi2, $Xi2, $Yi2, $C_Rectangle); imageline($this->picture, $point2->getX() - $radius, $Yi3, $Xi3, $Yi3, $C_Rectangle); imageline($this->picture, $Xi4, $Yi4, $point1->getX() + $radius, $Yi4, $C_Rectangle ); $this->drawAntialiasPixel(new Point($Xi1, $Yi1), $color, $shadowProperties); $this->drawAntialiasPixel(new Point($Xi2, $Yi2), $color, $shadowProperties); $this->drawAntialiasPixel(new Point($Xi3, $Yi3), $color, $shadowProperties); $this->drawAntialiasPixel(new Point($Xi4, $Yi4), $color, $shadowProperties); } imagefilledrectangle($this->picture, $point1->getX(), $point1->getY() + $radius, $point2->getX(), $point2->getY() - $radius, $C_Rectangle); imagefilledrectangle($this->picture, $point1->getX() + $radius, $point1->getY(), $point2->getX() - $radius, $point2->getY(), $C_Rectangle); $X1 = $point1->getX() - .2; $Y1 = $point1->getY() - .2; $X2 = $point2->getX() + .2; $Y2 = $point2->getY() + .2; $this->drawLine(new Point($X1 + $radius, $Y1), new Point($X2 - $radius, $Y1), $color, $lineWidth, $lineDotSize, $shadowProperties); $this->drawLine(new Point($X2, $Y1 + $radius), new Point($X2, $Y2 - $radius), $color, $lineWidth, $lineDotSize, $shadowProperties); $this->drawLine(new Point($X2 - $radius, $Y2), new Point($X1 + $radius, $Y2), $color, $lineWidth, $lineDotSize, $shadowProperties); $this->drawLine(new Point($X1, $Y2 - $radius), new Point($X1, $Y1 + $radius), $color, $lineWidth, $lineDotSize, $shadowProperties); } public function drawLine(Point $point1, Point $point2, Color $color, $lineWidth, $lineDotSize, ShadowProperties $shadowProperties, Point $boundingBoxMin = null, Point $boundingBoxMax = null) { if ($lineDotSize > 1) { $this->drawDottedLine($point1, $point2, $lineDotSize, $lineWidth, $color, $shadowProperties, $boundingBoxMin, $boundingBoxMax); return; } $Distance = $point1->distanceFrom($point2); if ($Distance == 0) return; $XStep = ($point2->getX() - $point1->getX()) / $Distance; $YStep = ($point2->getY() - $point1->getY()) / $Distance; for($i = 0; $i <= $Distance; $i ++) { $X = $i * $XStep + $point1->getX(); $Y = $i * $YStep + $point1->getY(); if ((($boundingBoxMin == null) || (($X >= $boundingBoxMin->getX()) && ($Y >= $boundingBoxMin->getY()))) && (($boundingBoxMax == null) || (($X <= $boundingBoxMax->getX()) && ($Y <= $boundingBoxMax->getY())))) { if ($lineWidth == 1) $this->drawAntialiasPixel(new Point($X, $Y), $color, $shadowProperties); else { $StartOffset = - ($lineWidth / 2); $EndOffset = ($lineWidth / 2); for($j = $StartOffset; $j <= $EndOffset; $j ++) $this->drawAntialiasPixel(new Point($X + $j, $Y + $j), $color, $shadowProperties); } } } } public function drawDottedLine(Point $point1, Point $point2, $dotSize, $lineWidth, Color $color, ShadowProperties $shadowProperties, Point $boundingBoxMin = null, Point $boundingBoxMax = null) { $Distance = $point1->distanceFrom($point2); $XStep = ($point2->getX() - $point1->getX()) / $Distance; $YStep = ($point2->getY() - $point1->getY()) / $Distance; $DotIndex = 0; for($i = 0; $i <= $Distance; $i ++) { $X = $i * $XStep + $point1->getX(); $Y = $i * $YStep + $point1->getY(); if ($DotIndex <= $dotSize) { if (($boundingBoxMin == null || (($X >= $boundingBoxMin->getX()) && ($Y >= $boundingBoxMin->getY()))) && ($boundingBoxMax == null || (($X <= $boundingBoxMax->getX()) && ($Y <= $boundingBoxMax->getY())))) { if ($lineWidth == 1) $this->drawAntialiasPixel(new Point($X, $Y), $color, $shadowProperties); else { $StartOffset = - ($lineWidth / 2); $EndOffset = ($lineWidth / 2); for($j = $StartOffset; $j <= $EndOffset; $j ++) { $this->drawAntialiasPixel(new Point($X + $j, $Y + $j), $color, $shadowProperties); } } } } $DotIndex ++; if ($DotIndex == $dotSize * 2) $DotIndex = 0; } } public function drawAntialiasPixel(Point $point, Color $color, ShadowProperties $shadowProperties, $alpha = 100) { /* Process shadows */ if ($shadowProperties->active) { $this->drawAntialiasPixel(new Point($point->getX() + $shadowProperties->xDistance, $point->getY() + $shadowProperties->yDistance), $shadowProperties->color, ShadowProperties::NoShadow(), $shadowProperties->alpha); if ($shadowProperties->blur != 0) { $AlphaDecay = ($shadowProperties->alpha / $shadowProperties->blur); for($i = 1; $i <= $shadowProperties->blur; $i ++) $this->drawAntialiasPixel(new Point($point->getX() + $shadowProperties->xDistance - $i / 2, $point->getY() + $shadowProperties->yDistance - $i / 2), $shadowProperties->color, ShadowProperties::NoShadow(), $shadowProperties->alpha - $AlphaDecay * $i); for($i = 1; $i <= $shadowProperties->blur; $i ++) $this->drawAntialiasPixel(new Point($point->getX() + $shadowProperties->xDistance + $i / 2, $point->getY() + $shadowProperties->yDistance + $i / 2), $shadowProperties->color, ShadowProperties::NoShadow(), $shadowProperties->alpha - $AlphaDecay * $i); } } $Xi = floor ( $point->getX() ); $Yi = floor ( $point->getY() ); if ($Xi == $point->getX() && $Yi == $point->getY()) { if ($alpha == 100) { $C_Aliased = $this->allocateColor($color); imagesetpixel ( $this->picture, $point->getX(), $point->getY(), $C_Aliased ); } else $this->drawAlphaPixel($point, $alpha, $color); } else { $Alpha1 = (((1 - ($point->getX() - $Xi)) * (1 - ($point->getY() - $Yi)) * 100) / 100) * $alpha; if ($Alpha1 > $this->antialiasQuality) { $this->drawAlphaPixel(new Point($Xi, $Yi), $Alpha1, $color); } $Alpha2 = ((($point->getX() - $Xi) * (1 - ($point->getY() - $Yi)) * 100) / 100) * $alpha; if ($Alpha2 > $this->antialiasQuality) { $this->drawAlphaPixel (new Point($Xi + 1, $Yi), $Alpha2, $color); } $Alpha3 = (((1 - ($point->getX() - $Xi)) * ($point->getY() - $Yi) * 100) / 100) * $alpha; if ($Alpha3 > $this->antialiasQuality) { $this->drawAlphaPixel (new Point($Xi, $Yi + 1), $Alpha3, $color); } $Alpha4 = ((($point->getX() - $Xi) * ($point->getY() - $Yi) * 100) / 100) * $alpha; if ($Alpha4 > $this->antialiasQuality) { $this->drawAlphaPixel (new Point($Xi + 1, $Yi + 1), $Alpha4, $color); } } } public function drawAlphaPixel(Point $point, $alpha, Color $color) { /** @todo Check that the point is within the bounds of the * canvas */ $RGB2 = imagecolorat ( $this->picture, $point->getX(), $point->getY()); $R2 = ($RGB2 >> 16) & 0xFF; $G2 = ($RGB2 >> 8) & 0xFF; $B2 = $RGB2 & 0xFF; $iAlpha = (100 - $alpha) / 100; $alpha = $alpha / 100; $Ra = floor ( $color->r * $alpha + $R2 * $iAlpha ); $Ga = floor ( $color->g * $alpha + $G2 * $iAlpha ); $Ba = floor ( $color->b * $alpha + $B2 * $iAlpha ); $C_Aliased = $this->allocateColor (new Color($Ra, $Ga, $Ba)); imagesetpixel($this->picture, $point->getX(), $point->getY(), $C_Aliased ); } /** * Color helper * * @todo This shouldn't need to be public, it's only a temporary * step while refactoring */ public function allocateColor(Color $color, $Factor = 0, $alpha = 100) { if ($Factor != 0) { $color = $color->addRGBIncrement($Factor); } if ($alpha == 100) { return (imagecolorallocate ($this->picture, $color->r, $color->g, $color->b )); } else { return imagecolorallocatealpha($this->picture, $color->r, $color->g, $color->b, 127 * (1 - $alpha / 100)); } } /** * @todo This is only a temporary interface while I'm * refactoring. This should eventually be removed. */ public function getPicture() { return $this->picture; } public function getAntialiasQuality() { return $this->antialiasQuality; } public function setAntialiasQuality($newQuality) { if (!is_numeric($newQuality) || $newQuality < 0 || $newQuality > 100) { throw new InvalidArgumentException("Invalid argument to GDCanvas::setAntialiasQuality()"); } $this->antialiasQuality = $newQuality; } function drawText($fontSize, $angle, Point $point, Color $color, $fontName, $text, ShadowProperties $shadowProperties) { if ($shadowProperties->active) { $gdShadowColor = $this->allocateColor($shadowProperties->color); imagettftext($this->picture, $fontSize, $angle, $point->getX() + $shadowProperties->xDistance, $point->getY() + $shadowProperties->yDistance, $gdShadowColor, $fontName, $text); } $gdColor = $this->allocateColor($color); imagettftext($this->picture, $fontSize, $angle, $point->getX(), $point->getY(), $gdColor, $fontName, $text); } function drawCircle(Point $center, $height, Color $color, ShadowProperties $shadowProperties, $width = null) { if ($width == null) { $width = $height; } $Step = 360 / (2 * M_PI * max ( $width, $height )); for($i = 0; $i <= 360; $i = $i + $Step) { $X = cos ( $i * M_PI / 180 ) * $height + $center->getX(); $Y = sin ( $i * M_PI / 180 ) * $width + $center->getY(); $this->drawAntialiasPixel(new Point($X, $Y), $color, $shadowProperties); } } public function drawFilledCircle(Point $center, $height, Color $color, ShadowProperties $shadowProperties, $width = null) { if ($width == null) { $width = $height; } $C_Circle = $this->allocateColor($color); $Step = 360 / (2 * M_PI * max ( $width, $height )); for($i = 90; $i <= 270; $i = $i + $Step) { $X1 = cos ( $i * M_PI / 180 ) * $height + $center->getX(); $Y1 = sin ( $i * M_PI / 180 ) * $width + $center->getY(); $X2 = cos ( (180 - $i) * M_PI / 180 ) * $height + $center->getX(); $Y2 = sin ( (180 - $i) * M_PI / 180 ) * $width + $center->getY(); $this->drawAntialiasPixel(new Point($X1 - 1, $Y1 - 1), $color, $shadowProperties); $this->drawAntialiasPixel(new Point($X2 - 1, $Y2 - 1), $color, $shadowProperties); if (($Y1 - 1) > $center->getY() - max ( $width, $height )) { imageline ( $this->picture, $X1, $Y1 - 1, $X2 - 1, $Y2 - 1, $C_Circle ); } } } function drawFilledPolygon(array $points, $numPoints, Color $color, $alpha = 100) { $gdColor = $this->allocateColor($color, 0, $alpha); imagefilledpolygon($this->picture, $points, $numPoints, $gdColor); } private $picture; /** * Quality of the antialiasing we do: 0 is maximum, 100 is minimum */ private $antialiasQuality; }