rotate + translate + draw = problem
Posted: 2012-04-21T09:43:55-07:00
Hi.
I want to add text with background to my thumbnails. I've came up with following solution:
(whole code: http://pastebin.com/dtXiEGFP)
When drawing horizontal bars everything is ok and works as expected, but when I'm trying to make vertical annotate method goes insane: left bar is drawn w/o couple of bottom pixels which are still available, right bar is drawn from x1 = 1-3 instead of 0.
Am I doing something wrong or its some issue of IMagick? Same code works with RMagick perfectly
I want to add text with background to my thumbnails. I've came up with following solution:
Code: Select all
const MiddleGravity = 0xDEADC0DE; /// own constant, doesn't get to IMagick methods
/// IMagick constants are aliased for better readability, i.e. CenterGravity is for imagick::GRAVITY_CENTER
protected static $translations = array(
self::WestGravity => self::SouthGravity,
self::MiddleGravity => self::CenterGravity,
self::EastGravity => self::NorthGravity
);
//....
public function thumbnail($options = array()){
$options += $this->config; /// merge default options with user passed
$options['width'] = min($options['width'], $this->image->getImageWidth()); // in case thumbnail dimensions are bigger than original image
$options['height'] = min($options['height'], $this->image->getImageHeight());
if($options['crop'])
$this->image->cropThumbnailImage($options['width'], $options['height']);
else
$this->image->thumbnailImage($options['width'], $options['height']);
if(!array_key_exists('text', $options) || empty($options['text']))
return;
$draw = new ImagickDraw();
$draw->setFont($options['font']);
$draw->setFontSize($options['size']);
$draw->setFillColor($this->color($options['background_color'])); /// color() is just a wrapper for new ImagickPixel($color);
$draw->setFontStyle($options['font_style']);
$draw->setFontWeight($options['font_weight']);
$draw->setStrokeColor($this->color('transparent'));
/// here comes the magick. in case when user wants vertical bars we just rotate drawing context and do translation of
/// coords then just draw corresponding horizontal bar
if(array_key_exists($options['gravity'], self::$translations)){
switch($options['text_direction']){ /// if user wants text to be written from top to bottom or bottom to top then we should change rotation angle
case self::TopToBottom:
$draw->translate($this->image->getImageHeight(), 0);
$draw->rotate(90);
break;
case self::BottomToTop:
$draw->translate(0, $this->image->getImageWidth());
$draw->rotate(270);
break;
default:
throw new ImageOperationError('Invalid text direction');
}
$options['gravity'] = self::$translations[$options['gravity']];
$thumb_height = $this->image->getImageWidth(); /// after rotation width and height are swapped
$thumb_width = $this->image->getImageHeight();
}else{
$thumb_width = $this->image->getImageWidth();
$thumb_height = $this->image->getImageHeight();
}
$metrics = $this->image->queryFontMetrics($draw, $options['text'], false); /// get text bounding box
$box_width = $metrics['textWidth'];
$box_height = round($metrics['boundingBox']['y2'] - $metrics['boundingBox']['y1']); /// thats tightest box that text fits to
if($box_width > $thumb_width - $options['padding'] * 2) /// if text is too wide
throw new ImageOperationError('Text too long');
$draw_text = $draw->clone(); /// save current context for text drawing
switch($options['text_align']){ /// where to start writing text (X)
case self::LeftAlign:
$text_x = $options['padding'];
break;
case self::CenterAlign:
$text_x = $thumb_width / 2 - $box_width / 2;
break;
case self::RightAlign:
$text_x = $thumb_width - $box_width - 1- $options['padding'];
break;
default:
throw new ImageOperationError('Invalid text align');
}
switch($options['gravity']){ /// now depending on user given gravity we should calculate where to draw background (Y) and text (Y)
case self::NorthGravity: /// top bar
$y1 = 0;
$y2 = $options['padding'] * 2 + $box_height;
$text_y = $options['padding'] + $box_height + $metrics['boundingBox']['y1'];
break;
case self::CenterGravity: /// on the middle
$y1 = round($thumb_height / 2 - $box_height / 2 - $options['padding']);
$y2 = round($thumb_height / 2 + $box_height / 2 + $options['padding']);
$text_y = round($thumb_height / 2 + $box_height / 2 + $metrics['boundingBox']['y1']);
break;
case self::SouthGravity: /// bottom one
$y1 = $thumb_height - $options['padding'] * 2 - $box_height;
$y2 = $thumb_height;
$text_y = $thumb_height - $options['padding'] + $metrics['boundingBox']['y1'];
break;
default:
throw new ImageOperationError('Invalid gravity');
}
if(array_key_exists('background', $options) && $options['background']){ /// should we draw background?
$draw->rectangle(0, $y1, $thumb_width, $y2 - 1);
$this->image->drawImage($draw);
}
$draw_text->setFillColor($options['color']);
$draw_text->annotation($text_x, $text_y, $options['text']);
$this->image->drawImage($draw_text);
}
When drawing horizontal bars everything is ok and works as expected, but when I'm trying to make vertical annotate method goes insane: left bar is drawn w/o couple of bottom pixels which are still available, right bar is drawn from x1 = 1-3 instead of 0.
Am I doing something wrong or its some issue of IMagick? Same code works with RMagick perfectly