Go Back   vb.org Archive > vBulletin Article Depository > Read An Article > vBulletin 3 Articles
[How-To] Code for ImageMagick
hambil's Avatar
hambil
Join Date: Jun 2004
Posts: 1,719

 

Seattle
Show Printable Version Email this Page Subscription
hambil hambil is offline 04-05-2009, 10:00 PM

Although vBulletin supports ImageMagick it is not widely supported in the mod community. In fact, when I ran into a need to support it for Egg Avatar I could not find another mod that did so to learn from.

After a lot of reading and poking around I discovered that, although the vBulletin image class that supports ImageMagick is far to specific to be useful, it does have one rather useful member: fetch_im_exec().

fetch_im_exec takes care of finding and calling ImageMagick for you, all you need to do is pass the proper command line.

Of course, I didn't want to fill my code with if/else statements every time I wanted to do something with an image, so it made sense to create my own class to abstract the process.

Step 1: Creating an Abstract Base Class
The idea here is that we are basically creating an 'interface'. A common set of methods that can be called without having to worry about whether GD or ImageMagick is the image processor.

PHP Code:
/**
* Abstracted tk image class
*/
class tk_Image_Abstract
{
    
/**
    * Main data registry
    *
    * @var    vB_Registry
    */
    
var $registry null;

    
/**
    * Constructor
    * Don't allow direct construction of this abstract class
    * Sets registry
    *
    * @return    void
    */
    
function tk_Image_Abstract(&$registry)
    {
        if (!
is_subclass_of($this'tk_Image_Abstract'))
        {
            
trigger_error('Direct Instantiation of tk_Image_Abstract prohibited.'E_USER_ERROR);
            return 
NULL;
        }

        
$this->registry = &$registry;
    }

    
/**
    * Public functions
    */

    
function fetch_width($himage) {}
    function 
fetch_height($himage) {}
    function 
fetch_image($url) {}
    function 
fetch_image_from_string($string) {}
    function 
output($himage) {}
    function 
create_truecolor($imgwidth$imgheight$himage) {}
    function 
load_egg($url) {}
    function 
copy_image($hdst$hsrc$dstx$dsty$srcx$srcy$srcwidth$srcheight) {}
    function 
destroy_image($himage) {}
    function 
process_for_output($himage) {}

You can see that the class is rather simple. It holds the registry ($vbulletin), does not allow itself to be instantiated directly, and contains a bunch of empty functions as 'placeholders'. These empty functions are important though, but the reason is probably beyond the scope of this article.

Step 2: Create a 'factory class'
A factory class is a simple little class that checks some values and creates the proper real class for you. In this case I check to see if vbulletin is set to use GD or ImageMagick and return an instance of either tk_Image_GD or tk_Image_ImageMagick.

PHP Code:
class tk_Image
{
    static 
$instance;

    function 
get_instance($registry)
    {
        if (
$instance)
        {
          return 
$instance;
        }
        else
        {
            
$postfix = ($registry->options['imagetype'] ? $registry->options['imagetype'] : 'GD');
            eval(
'$instance =& tk_Image_' $postfix '::get_instance($registry);');
            return 
$instance;
        }
    }

Step 3: The real classes
The full code of each of these classes can be seen in the attached zip, but we're going to take a look at a stripped down version of each that implements the 'meat' method of an image processing, that is: fetch_image.

We'll look at the GD example first, because it's simpler.

PHP Code:
/**
* Image class for GD Image Library
*/
class tk_Image_GD extends tk_Image_Abstract
{
    
/**
     * Constructor. Sets up resizable types, extensions, etc.
     *
     * @return    void
     */
     
function tk_Image_GD(&$registry)
    {
    
parent::tk_Image_Abstract($registry);
    }

    function 
get_instance($registry)
    {
        return new 
tk_Image_GD($registry);
    }
    
    
/**
     * Public functions
     */

    
function fetch_image($url
    {
        
$size getimagesize($url);
        
$mime $size['mime'];
        if (
strpos($mime"jpeg") !== false)
        {
            
$img imagecreatefromjpeg($url);
        }
        elseif (
strpos($mime"png") !== false)
        {
            
$img imagecreatefrompng($url);
        }
        else
        {
            
$img imagecreatefromgif($url);
        }
        
        return 
$img;      
    }

Anyone who's dealt with image handling in GD will recognize this. We determine the type of image and use one of GD's imagecreate methods to load the image into memory and return a handle to it. For my needs I only handle jpeg, gif and png - you might need to add more types.

ImageMagick is in someways simple, but also trickier. It has no concept of working in memory or 'handles'. Everything is done with files on disk. So we have to 'fake' the process by creating an array of filenames we are working with and return positions in that array as the 'handles'.

Since the ImageMagick calls we will make later will directly alter the image, we need to make a temporary copy instead, to preserve the original (at least, for my purposes I did not want to alter the original).

PHP Code:
/**
* Image class for ImageMagick
*/
class tk_Image_Magick extends tk_Image_Abstract
{
    var 
$vbimagemk;
    var 
$is_ani false;
    static 
$images = array();
    
    function 
tk_Image_Magick(&$registry)
   {
    
parent::tk_Image_Abstract($registry);
        
$this->vbimagemk = new vb_Image_Magick($registry);
    }

    function 
get_instance($registry)
    {
        return new 
tk_Image_Magick($registry);
    }
    
    function 
check_error()
    {
        if (
$error $this->vbimagemk->fetch_error())
        {
            echo 
$error;
            die();
        }
    }

   
/**
    * Public functions
    */

    
function fetch_image_from_string($string
    {
        
// write a temp file
        
$tmpfname tempnam("/tmp"'tk_');
        
$handle fopen($tmpfname"w");
        
fwrite($handle$string);
        
fclose($handle);
        
        
// determine the file type
        
$args $tmpfname;
        
$fileinfo $this->vbimagemk->fetch_im_exec('identify'$argstrue);
        
$this->check_error();        
        
$fileinfo explode(' '$fileinfo[0]);
        
$ext $fileinfo[1];
        
        
// rename the file to add extension
        
rename($tmpfname$tmpfname '.' $ext);
        
$this->images[] .= $tmpfname '.' $ext;
        return 
count($this->images) - 1;       
    }

This method creates a temporary files, adds its name to the image array, and returns the array position as the 'handle' for later use. You can also see the first use of fetch_im_exec. It's really quite easy. The trick is reading up and learning the proper arguments to pass into ImageMagick to do what you want.

ImageMagick consists of two main 'executables', identify and covert. identify gathers information about an image, and convert alters it. Here I use identify to find out what type of image I'm working with to properly give the temp file an extensions of 'gif' or 'jpg' or whatever.

The second post in this article contains a full usage example of the zipped class I've provided. The class won't cover all your needs since it was written for mine, but it should be easy for you to adapt or add to it for whatever your needs are.
Attached Files
File Type: zip class_tkimage.zip (2.2 KB, 16 views)
Reply With Quote
  #2  
Old 04-06-2009, 03:39 PM
hambil's Avatar
hambil hambil is offline
 
Join Date: Jun 2004
Location: Seattle
Posts: 1,719
Благодарил(а): 0 раз(а)
Поблагодарили: 0 раз(а) в 0 сообщениях
Default

Usage example:

PHP Code:
<?php

// ####################### SET PHP ENVIRONMENT ###########################
error_reporting(E_ALL & ~E_NOTICE);

// #################### DEFINE IMPORTANT CONSTANTS #######################
define('THIS_SCRIPT''tkimage_test');

// ######################### REQUIRE BCK-END ############################
require_once('./global.php');
require_once(
DIR '/includes/class_tkimage.php');

// ###################### Load the image handler ########################
$imagehandler tk_Image::get_instance($vbulletin);

// get the avatar
$hdst $imagehandler->fetch_image('avatar.gif');
$dwidth $imagehandler->fetch_width($hdst);
$dheight $imagehandler->fetch_height($hdst);

// create a backdrop 
$htmp $imagehandler->create_truecolor($dwidth$dheight$hdst);

// get the 'egg'
$hsrc $imagehandler->fetch_image('egg.gif');
$swidth $imagehandler->fetch_width($hsrc);
$sheight $imagehandler->fetch_height($hsrc);

// copy it onto the avatar/backdrop
$imagehandler->copy_image($htmp$hsrc101000$swidth$sheight);

// prepare it and output it
$imagehandler->process_for_output($htmp);
$imagehandler->output($htmp);

// clean up
$imagehandler->destroy_image($hsrc);
$imagehandler->destroy_image($hdst);
$imagehandler->destroy_image($htmp);

?>
Reply With Quote
  #3  
Old 04-16-2009, 03:33 PM
TheLastSuperman's Avatar
TheLastSuperman TheLastSuperman is offline
Senior Member
 
Join Date: Sep 2008
Location: North Carolina
Posts: 5,844
Благодарил(а): 0 раз(а)
Поблагодарили: 0 раз(а) в 0 сообщениях
Default

Very very useful info, thanks for spending the time getting this posted

S-MAN
Reply With Quote
  #4  
Old 04-18-2009, 02:56 PM
HyperActiv HyperActiv is offline
 
Join Date: Mar 2009
Location: Israel
Posts: 33
Благодарил(а): 0 раз(а)
Поблагодарили: 0 раз(а) в 0 сообщениях
Default

wow.. useful! thanks!
Reply With Quote
  #5  
Old 05-08-2009, 04:32 AM
AndrewD AndrewD is offline
 
Join Date: Jul 2002
Location: Scotland
Posts: 3,486
Благодарил(а): 0 раз(а)
Поблагодарили: 0 раз(а) в 0 сообщениях
Default

Very nice and helpful, thanks.

NB: There are small bugs in the zip file -
a) the output function has different numbers of arguments for the gd and imagemagick classes - I assume that the imagemagick version is what was intended.
b) the imagemagick code does not work correctly if filenames contain spaces
Reply With Quote
Reply

Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off

Forum Jump


All times are GMT. The time now is 03:02 PM.


Powered by vBulletin® Version 3.8.12 by vBS
Copyright ©2000 - 2025, vBulletin Solutions Inc.
X vBulletin 3.8.12 by vBS Debug Information
  • Page Generation 0.04611 seconds
  • Memory Usage 2,312KB
  • Queries Executed 19 (?)
More Information
Template Usage:
  • (1)SHOWTHREAD
  • (1)ad_footer_end
  • (1)ad_footer_start
  • (1)ad_header_end
  • (1)ad_header_logo
  • (1)ad_navbar_below
  • (1)ad_showthread_beforeqr
  • (5)bbcode_php
  • (1)footer
  • (1)forumjump
  • (1)forumrules
  • (1)gobutton
  • (1)header
  • (1)headinclude
  • (1)modsystem_article
  • (1)navbar
  • (4)navbar_link
  • (120)option
  • (5)post_thanks_box
  • (5)post_thanks_button
  • (1)post_thanks_javascript
  • (1)post_thanks_navbar_search
  • (5)post_thanks_postbit_info
  • (4)postbit
  • (1)postbit_attachment
  • (5)postbit_onlinestatus
  • (5)postbit_wrapper
  • (1)spacer_close
  • (1)spacer_open
  • (1)tagbit_wrapper 

Phrase Groups Available:
  • global
  • inlinemod
  • postbit
  • posting
  • reputationlevel
  • showthread
Included Files:
  • ./showthread.php
  • ./global.php
  • ./includes/init.php
  • ./includes/class_core.php
  • ./includes/config.php
  • ./includes/functions.php
  • ./includes/class_hook.php
  • ./includes/modsystem_functions.php
  • ./includes/functions_bigthree.php
  • ./includes/class_postbit.php
  • ./includes/class_bbcode.php
  • ./includes/functions_reputation.php
  • ./includes/functions_post_thanks.php 

Hooks Called:
  • init_startup
  • init_startup_session_setup_start
  • init_startup_session_setup_complete
  • cache_permissions
  • fetch_threadinfo_query
  • fetch_threadinfo
  • fetch_foruminfo
  • style_fetch
  • cache_templates
  • global_start
  • parse_templates
  • global_setup_complete
  • showthread_start
  • showthread_getinfo
  • forumjump
  • showthread_post_start
  • showthread_query_postids
  • showthread_query
  • bbcode_fetch_tags
  • bbcode_create
  • showthread_postbit_create
  • postbit_factory
  • postbit_display_start
  • post_thanks_function_post_thanks_off_start
  • post_thanks_function_post_thanks_off_end
  • post_thanks_function_fetch_thanks_start
  • post_thanks_function_fetch_thanks_end
  • post_thanks_function_thanked_already_start
  • post_thanks_function_thanked_already_end
  • fetch_musername
  • postbit_imicons
  • bbcode_parse_start
  • bbcode_parse_complete_precache
  • bbcode_parse_complete
  • postbit_attachment
  • postbit_display_complete
  • post_thanks_function_can_thank_this_post_start
  • tag_fetchbit_complete
  • forumrules
  • navbits
  • navbits_complete
  • showthread_complete