Rendering pets as layers?

Dinocanid

Artist
I've heard this is a pretty well-kept secret, so I might not have any luck, but how would you go about using layers for pets? (lineart, bases, markings, etc.)

I've recently gotten Imagick and I'm playing around with it on my XAMMP server (Can use GD also), but I'm not really sure how to do this particular thing. I know the different markings and such are stored in the database, but where is the image handled? Is it done in another php file and called where you want it? How would that work for multiple pets and users? I haven't found a way to both render an image and display it along with other content in the same file, if that's even possible.

 
I am not sure if @Hare would be up to help more.

So, basically you are merging layers together into a new image. You determine the layers to compose together. You are basically piecing parts of it together.

A simple (untested) version could look like:

<?php
// Remember to order these in reverse, the last element in the array should always be the top layer you will see (usually lineart). All images should be the same dimensions.
$images = array(
'path/to/base.png',
'path/to/marking.png',
'path/to/lineart.png'
);

// This creates the Imagick class that we will use.
$composed_image = new \Imagick($images);

// Now we can actually do some fun stuff such as color or tint the layers.
// For instance, let's tint the base. I am using random (probably awful colors)

// Base
$composed_image->setIteratorIndex(0);
$composed_image->colorizeImage(new \ImagickPixel("rgba(24,35,22,1)"), new \ImagickPixel("rgba(128,128,128,1"));
$composed_image->tintImage(new \ImagickPixel("rgba(24,35,22,1)"), new \ImagickPixel("rgba(128,128,128,1"));

// As you see above, by calling setIteratorIndex(), you switch your "working layer" to the layer you wish to modify.
// Now lets flatten it and display it. This creates a new Imagick instance to work with with only one flat image.
$image = $composed_image->mergeImageLayers(\Imagick::LAYERMETHOD_FLATTEN);
$image->setImageFormat('png');

header('Content-type: image/png');
echo $image->getImageBlob();




Hopefully this helps to provide a little more insight. The documentation is rather useful.

 
With Imagemagick, you want to have your file (let's say petimage.php) where all of the imagemagick code goes, like Digital posted.

Then you echo out that file as your img src URL.

Example: <img src='petimage.php'>

You can use $_GET variables to pass data from your database into the petimage.php file. 

 
Last edited by a moderator:
Thank you both, it works great! What would I do if I wanted to use Imagick:: COMPOSITE_OVERLAY? This is what I have (modified from php.net and using the above code):

$composed_image->setIteratorIndex(1);
$composed_image->compositeImage( $images[0], \Imagick::COMPOSITE_OVERLAY, 0, 0 );


It returns this error though (lines 17-18 are the ones above): 

Catchable fatal error: Argument 1 passed to Imagick::compositeimage() must be an instance of Imagick, string given in C:\xampp\htdocs\wild-souls\blank.php on line 18
Would it be better to take the image paths out of an array and just define them as variables?

 
$composed_image->compositeImage( new \Imagick($images[0]), \Imagick::COMPOSITE_OVERLAY, 0, 0 );


You can try the above. Basically the error is saying that you are passing in a string (the image path) instead of a Imagick object. Bascially just taking the image and creating an instance will solve it.

 
It works! Thanks again. I played around with the different composite types and ended up using Imagick::COMPOSITE_DSTIN since my markings aren't "clean" and go outside of the lines otherwise.

 
One last question (I'm probably asking too many of these), what about generating the image? Like I do the above and call the image from the desired page, but everyone's pets won't look the same so I would have to change the layers in "petimage.php" somehow each time a user visits its profile page. Let's say I use ajax, if a user clicks on that particular pet's name from the list, the profile page will display the respective generated image. In petimage.php, instead of the image paths in an array, would it be variables gotten from the database with the row picked with the pet's ID #? (So if there's a column named "marking1", it would contain the image path)

 
One last question (I'm probably asking too many of these), what about generating the image? Like I do the above and call the image from the desired page, but everyone's pets won't look the same so I would have to change the layers in "petimage.php" somehow each time a user visits its profile page. Let's say I use ajax, if a user clicks on that particular pet's name from the list, the profile page will display the respective generated image. In petimage.php, instead of the image paths in an array, would it be variables gotten from the database with the row picked with the pet's ID #? (So if there's a column named "marking1", it would contain the image path)
Yeah, you can use $_GET for that. Have your $_GET variables at the top of the imagemagick file and use them to determine which layers are rendered (and how). 

Say you want to render the image on a pet's profile. This pet is a Cat with orange markings. 

You echo  <img src='petimage.php?species=cat&markings=orange'>

I'm not real familiar with ajax. What I do is put the image files in a pet image folder, and have the names of the files be in the database. So you'd have a folder for each pet species, and each one has a lineart.png in it for example. And then each species' folder also has the markings and whatnot. Keep the names consistent accross all species folders to make sure your imagemagick code and database are synced up.

 
Last edited by a moderator:
Yeah, you can use $_GET for that. Have your $_GET variables at the top of the imagemagick file and use them to determine which layers are rendered (and how). 

Say you want to render the image on a pet's profile. This pet is a Cat with orange markings. 

You echo  <img src='petimage.php?species=cat&markings=orange'>

I'm not real familiar with ajax. What I do is put the image files in a pet image folder, and have the names of the files be in the database. So you'd have a folder for each pet species, and each one has a lineart.png in it for example. And then each species' folder also has the markings and whatnot. Keep the names consistent accross all species folders to make sure your imagemagick code and database are synced up.
You can use also use a variable that is a pet's id in a database or anything else to get the information if you don't want to pass all the options in, or you have very unique customizations going on.

 
You can use also use a variable that is a pet's id in a database or anything else to get the information if you don't want to pass all the options in, or you have very unique customizations going on.
Ooh, I want to know about this. =o I noticed (at least from what I can tell) that queries do not seem to work in Imagemagick files. So if you were to only $_GET the pet's ID, how would you go about getting species/color info? Is there a special kind of query or other trick to it? 

I can see how just passing the ID through $_GET would work for customizations if you have the images for those saved by pet ID.

 
Last edited by a moderator:
Ooh, I want to know about this. =o I noticed (at least from what I can tell) that queries do not seem to work in Imagemagick files. So if you were to only $_GET the pet's ID, how would you go about getting species/color info? Is there a special kind of query or other trick to it? 

I can see how just passing the ID through $_GET would work for customizations if you have the images for those saved by pet ID.
The idea is not to save the image (you could cache them if you wanted to), but rather store the details of the pet in a database for retrieval and rebuild the image dynamically off of that. if you concerned about performance, you could apply caching, however that is a more advanced topic and really just wraps around the generation.

A very simple process is to have a pet with say 3 columns that identify them. species, marking, and color. You can even have several different tables, but in the example here, let's keep it simple. If enough people ask, I can actually provide a more complex structure that I used in my game in development.

Below is the script from above with some added PDO (PHP's database layer) to query for pet information before we render.

<?php

// First lets see if we have an "pet_id" added to the query string.
if(!isset($_GET['pet_id']))
die('We need an id!'); // if this page is called in am img tag, it will appear broken.

// Not included in this example is any validation that pet_id is a number or safe.

// Lets setup the database.
$host = '127.0.0.1';
$db = 'test';
$user = 'root';
$pass = '';
$charset = 'utf8';

$dsn = "mysql:host=$host;dbname=$db;charset=$charset";
$opt = [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_EMULATE_PREPARES => false,
];
$pdo = new PDO($dsn, $user, $pass, $opt);

// Lets get the pet information.
// Note, the pet table has 3 columns + id in this example for customization:
// pet_id = INT
// species - VARCHAR
// markings - VARCHAR - contains a marking type to include
// color - VARCHAR - contains a string in a ImagicPixel format (i.e. "rgba(125,125,125,1)")
// Your pet table would normally contain name or other fields, we are keeping simple in this example.

$stmt = $pdo->prepare('SELECT * FROM pets WHERE pet_id = ?');
$stmt->execute([$_GET['pet_id']]);
$pet = $stmt->fetch();

// Remember to order these in reverse, the last element in the array should always be the top layer you will see (usually lineart). All images should be the same dimensions.
// Note that we are using variables from our table to build the correct images we would need for this pet.
$images = array(
'path/to/' . $pet['species'] . '/base.png',
'path/to/' . $pet['species'] . '/' . $pet['markings'] . '.png',
'path/to/' . $pet['species'] . '/lineart.png'
);

// This creates the Imagick class that we will use.
$composed_image = new \Imagick($images);

// Now we can actually do some fun stuff such as color or tint the layers.
// For instance, let's tint the base. I am using random (probably awful colors)

// Base
$composed_image->setIteratorIndex(0);
$composed_image->colorizeImage(new \ImagickPixel($pet['color']), new \ImagickPixel("rgba(128,128,128,1"));
$composed_image->tintImage(new \ImagickPixel($pet['color']), new \ImagickPixel("rgba(128,128,128,1"));

// As you see above, by calling setIteratorIndex(), you switch your "working layer" to the layer you wish to modify.
// Now lets flatten it and display it. This creates a new Imagick instance to work with with only one flat image.
$image = $composed_image->mergeImageLayers(\Imagick::LAYERMETHOD_FLATTEN);
$image->setImageFormat('png');

header('Content-type: image/png');
echo $image->getImageBlob();


The main point is to store what you need to reconstruct the pets image. This sample means one script can display really any species of pet, with a different base color and markings layer.

@Hare I hope this is what you are asking about?

 
What version of ImageMagick are you using? It seems that Imagick PHP 5.4 can't find relative paths, or at least I can't get it to. I tried using absolute paths (C:\xampp), but that seems to break the code:

$images = array(
'C:\xampp\htdocs\wild-souls\picuploads\wolf_images\pup\bases\' . $pet['base'] . 'base-pup.png',
'C:\xampp\htdocs\wild-souls\picuploads\wolf_images\pup\markings\' . $pet['marking1'] . '-pup.png',
'C:\xampp\htdocs\wild-souls\picuploads\wolf_images\pup\lineart-pup.png'
);


(My naming conventions for these files are usually things like "brown base-pup.png" or " white ears-pup.png")

Absolute paths work without the variables in-between, but I need those unless I use a very long switch statement.

 
What version of ImageMagick are you using? It seems that Imagick PHP 5.4 can't find relative paths, or at least I can't get it to. I tried using absolute paths (C:\xampp), but that seems to break the code:

$images = array(
'C:\xampp\htdocs\wild-souls\picuploads\wolf_images\pup\bases\' . $pet['base'] . 'base-pup.png',
'C:\xampp\htdocs\wild-souls\picuploads\wolf_images\pup\markings\' . $pet['marking1'] . '-pup.png',
'C:\xampp\htdocs\wild-souls\picuploads\wolf_images\pup\lineart-pup.png'
);


(My naming conventions for these files are usually things like "brown base-pup.png" or " white ears-pup.png")

Absolute paths work without the variables in-between, but I need those unless I use a very long switch statement.
Escape your \ characters. So they would look like:

Code:
$images = array(
	'C:\\xampp\\htdocs\\wild-souls\\picuploads\\wolf_images\\pup\\bases\\' . $pet['base'] . 'base-pup.png',
	'C:\\xampp\\htdocs\\wild-souls\\picuploads\\wolf_images\\pup\\markings\\' . $pet['marking1'] . '-pup.png',
	'C:\\xampp\\htdocs\\wild-souls\\picuploads\\wolf_images\\pup\\lineart-pup.png'
);
 
After modifying the database statement, it works! Here's how it looks:

728a6d95b42d88eb09876aa7f998a0f0.jpeg.483883aa1078974d10410b890727dc32.jpeg
Thanks a lot you two, seriously. I couldn't figure it out myself  x_x

(The given PDO statement works, but mysidia has a simplified version so I used that)

 
Last edited by a moderator:
Oh my goodness, thank you Digital! I didn't know you could do it that way with PDO. =D

I'm glad we could help out, too!

 
Oh my goodness, thank you Digital! I didn't know you could do it that way with PDO. =D
In my game (inactive development), I actually moved all the setup for a species into a database. That way I could actually add new species without having to edit code unless I wanted to add something more to all species. I figured it made more sense.

Usually by separating your data (species colors or layers) into the database, you end up with very manageable code.

 
In my game (inactive development), I actually moved all the setup for a species into a database. That way I could actually add new species without having to edit code unless I wanted to add something more to all species. I figured it made more sense.

Usually by separating your data (species colors or layers) into the database, you end up with very manageable code.
I second this since I do it as well. Storing the different species and such in the database makes it easier to add new ones and keeps the code cleaner. (You can even code it to where you can add species on-site through an admin area like it is on mysidia!)

 
In my game (inactive development), I actually moved all the setup for a species into a database. That way I could actually add new species without having to edit code unless I wanted to add something more to all species. I figured it made more sense.

Usually by separating your data (species colors or layers) into the database, you end up with very manageable code.
That's a great way to do it! =D

 
Last edited by a moderator:
Back
Top