Jump to content

Rendering pets as layers?


Dinocanid

Recommended Posts

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.

Link to comment
Share on other sites

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.

Link to comment
Share on other sites

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. 

Edited by Hare
  • Like 1
  • Thanks 1
Link to comment
Share on other sites

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): 

Quote

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?

Link to comment
Share on other sites

$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.

Link to comment
Share on other sites

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)

Link to comment
Share on other sites

2 hours ago, Dinocanid said:

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.

 

Edited by Hare
  • Thanks 1
Link to comment
Share on other sites

7 hours ago, Hare said:

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.

Link to comment
Share on other sites

38 minutes ago, Digital said:

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.

Edited by Hare
Link to comment
Share on other sites

1 minute ago, Hare said:

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?

Link to comment
Share on other sites

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.

Link to comment
Share on other sites

1 hour ago, Dinocanid said:

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:

$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'
);

 

Link to comment
Share on other sites

After modifying the database statement, it works! Here's how it looks:

Spoiler

6121302820913152.png?k=BYDuYoFEgCxBacU99

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)

Edited by Dinocanid
Link to comment
Share on other sites

6 hours ago, Hare said:

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.

Link to comment
Share on other sites

20 hours ago, Digital said:

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!)

Link to comment
Share on other sites

22 hours ago, Digital said:

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

Edited by Hare
Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...