views:

779

answers:

9

i have

  1. A bunch of fabric patterns (simple jpg files)
  2. An image for every letter of the alphabet(blank white background)

I essentially want to have a page similar to this: http://www.craftcuts.com/hand-painted-wooden-letters-single-patterns.html

but instead of having it as a static page, i would like a user to be able to:

  1. Type in a name
  2. Choose a pattern (one of the jpg files)

and then have it display that name in that pattern.

Obviously i could create separate jpgs for every combination of letters (right now i have a jpg for every letter with white back color) and patterns but i wanted to find out if there was a more elegant way of coding this up to have it dynamically put one image onto the letter in the other.


EDIT: In my initial post, i assumed that this would have to be a front end thing (javascript), but if it makes it any easier (as a few people asked what is the backend), my back end is an asp.net-mvc so if there is some solution to build this up on the serverside and ship down to the client, i am more than happy using that as well.

+6  A: 

The easiest way to do so via Javascript is probably to have an image mask of each letter and apply it on top of a pattern. By image mask I mean a simple image completely black (or white, or whatever you prefer) with a "cut out" transparent part in the shape of the letter. You can simply overlay that over the pattern file to get the wanted effect.

<div style="background: url(pattern.jpg)">
    <img src="letter_a.png" />
</div>

You can dynamically change the img src and div background url to switch patterns and letters. You can also dynamically create new divs based on user input.

Note that this won't work easily in IE6 due to the transparent PNG.

Alternatively, you could generate the image dynamically on the server using something like gd, which would be a little more involved, but ultimately more flexible.

deceze
i dont quite follow here. do you have any links that show examples of this?
ooo
1. Put the pattern in the background. 2. Put a "cut out" image on top. 3. See the pattern through the holes in the "cut out". Sort of related example here: http://ifohdesigns.com/blog/tutorials/transparent-png-mask
deceze
While this way and transparant png's certainly provide a clean and flexible way, it won't allow you to use variated decorated letters as seen in the example where the letters not only differ in pattern but in shape as well.
richard
@richard Huh? What about making different letters? `letter_a_helvetica.png`, `letter_a_courier.png`...? Your suggestion would exponentially increase the number of combinations you need to make (letters * fonts * patterns), exactly what the OP didn't want to do. With my suggestion you just need to make a new set of letters for each font (letters * fonts + patterns). As I said, the optimal way would of course be totally automated generation of letters/fonts/patterns with an image manipulation library, but it has it's own pitfalls and is a more advanced technique.
deceze
+11  A: 

You could use the ImageMagick libraries to make any combination of superimpositions your and your users' hearts desire. This way, you're not limited to just something simple that can be achieved with CSS.

ImageMagick examples

Artem Russakovskii
are you suggesting that i use the composite image in the link?
ooo
Something like that. I used ImageMagick to implement watermarking, which is very similar to this.
Artem Russakovskii
@oo: Artem is suggesting that you generate the images on the server using ImageMagick, presumably after the user requests a certain combination of text and pattern. This can be done fairly easily with ImageMagick.
Mr. Shiny and New
Sounds quite a bit more complicated than the transparent masking solution.
Tchalvak
+1 for using ImageMagick - did the same way back for some projects. It is super powerful and easy to use in virtually any server environment.
Till
+4  A: 

For users to be able to type in their own name you'll need a text field. You'll also need a button that fires a Javascript function that checks the value of the text field and renders the letter images.

You can use the following code to create the letters. You will need a textfield with the id 'textfield' and a div to render the results in with the id 'output', which ofcourse you can change. I recommend using a select element to store the pattern options (#patternChooser in this example)

function renderName()
{
    // Get the name to be rendered and the chosen pattern
    var text = document.getElementById('textfield').value;
    var pattern = document.getElementById('patternChooser').value;

    // Iterate over the name and create an element for each letter
    for(i = 0; i < text.length; i++)
    {
     var letter = document.createElement('div');
     letter.style.backgroundImage = 'url(' + pattern + '_' + text[i] + ')';

     document.getElementById('output').appendChild(letter);
    }
}

You can use the following CSS to apply some positioning to the letters (adjust the with and height to that of your images):

#output div
{
 margin-left: 10px;
 width: 50px;
 height: 100px;
 float: left;
}

You will need to name your images like this: flowerpattern_a.png, brickpattern_j.png, etc.

If you want the letters to appear real time you can use Javascript's onkeyup() to fire a function that checks the last character of the text field's value and creates an element for it.

Sprites

You can also use a sprite to increase performance. Put all the images for the letters into one image. You set this image as a background for every letter. Quick read on CSS sprites: http://css-tricks.com/css-sprites/ You can add background-image: url(sprite.png); to the CSS snippet above. Instead of just setting a backgroundImage with Javascript, you will need to set the background position of the letter (letter.style.background-position = '100px 200px')

Font embedding

If you got fonts to use: there are many font embedding options like Typeface and Cufon. The one I find most pleasant to work with is the use of font-face. It is fast and the text will behave like any other text.

If you got your .TTF Truetype font, you'll need to convert the font to .EOT for use with Internet Explorer. You could also add SVG fonts for full browser coverage. It's actually very easy: You just add the following snippet to the top of your stylesheet like this:

@font-face {
  font-family: 'GothicCustom';
    src: url("LeagueGothic.eot");
    src: local('League Gothic'),
       url("LeagueGothic.svg#lg") format('svg'),
       url("LeagueGothic.otf") format('opentype');
}

The advantage of this technique is that it's easy to use and you got full controll over the rendered text, like any other text on your website. You can set the font-size, letter-spacing etc. Here's a good read about font-face font embedding. You can use Microsoft WEFT or TTF2EOT to create .EOT fonts.

For example, your code could look like this.

Javascript

function renderName()
{
    // Get the name to be rendered and the chosen pattern
    var text = document.getElementById('textfield').value;
    var pattern = document.getElementById('patternChooser').value;

    // Render the text with a class
    for(i = 0; i < text.length; i++)
    {
        var output = document.getElementById('output');

        output.style.fontFamily = pattern;
        output.innerHTML = text;
    }
}

HTML

<form>
    <input id="textfield" type="text" />

    <select id="patternChooser">
        <option>Flowers</option>
        <option>Brick</option>
        <option>Decorative</option>
    </select>

    <input type="button" onclick="renderName()" />
</form>

<div id="output"></div>

CSS

@font-face {
  font-family: 'Decorative';
    src: url("decorative.eot");
    src: local('Decorative'),
       url("Decorative.svg#lg") format('svg'),
       url("Decorative.otf") format('opentype');
}

Now the only thing that is left is converting the fonts and import them in your stylesheet.

Ofcourse you can choose to go with any other font-embedding method. Here's an article about font-embedding options but a quick Google will show you the options as well.

richard
Aren't you basically suggesting, with the addition of sprites, the approach the OP is trying to find a more elegant solution than?
philfreo
i think i agree . . in your example do i need every combination of letters and pattern pre setup?
ooo
In this example you'll need to save a png for every letter in the pattern or create a sprite for each pattern. I added the option of font-embedding with which you'll only need to offer the fonts and import them in your stylesheet.
richard
A: 

Read the link below here you will find the dynamic loader details alonmg with example http://thecodecentral.com/2008/02/21/a-useful-javascript-image-loader

Sachin Chourasiya
+1  A: 

If you are allowed to use something in the back-end, here is a java solution. You will just need the font (name.ttf) with which you type the letters, but I assume you have it:

(this is inside a servlet doGet method)

String text = "A letter, or any text here";
File texture = new File(patternTexturePath);

File fontFile = new File(filePath);
Font font = Font.createFont(Font.TRUETYPE_FONT, fontFile);
GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
ge.registerFont(font);


BufferedImage buffer = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);

g2 = buffer.createGraphics();
g2.clearRect(0, 0, width, height);
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                    RenderingHints.VALUE_ANTIALIAS_ON);
g2.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,
                    RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
g2.setRenderingHint(RenderingHints.KEY_RENDERING,
                      RenderingHints.VALUE_RENDER_QUALITY);

FontRenderContext fc = g2.getFontRenderContext();
Rectangle2D bounds = font.getStringBounds(text, fc);

AttributedString attrStr = new AttributedString(text);
Rectangle r = new Rectangle(0, 0, width, height);
attrStr.addAttribute(TextAttribute.FOREGROUND, new TexturePaint(ImageIO.read(texture), r));
attrStr.addAttribute(TextAttribute.FONT, font);
g2.drawString(attrStr.getIterator(), 0, (int) -bounds.getY());

response.setContentType("image/png");
ImageIO.write(buffer, "png", response.getOutputStream());
response.getOutputStream().close();
Bozho
+15  A: 

You can apply background images to images using CSS, then use a load of transparent .png images for the letters.

I mocked it up:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"&gt;
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Untitled Document</title>

<style type="text/css">

 img.leaves
 {
    background:url("leaves.png");
 }
 img.lights
 {
    background:url("lights.png");
 }

</style>

<script type="text/javascript">

function makeText()
{
    var text = document.getElementById('text').value.toUpperCase();
    var pattern = document.getElementById('pattern').value;

    for(var i=0; i<text.length; i++)
    {
        var img = document.createElement('img');
        img.src=text[i]+'.png';
        img.className=pattern;
        document.getElementById('textArea').appendChild(img);
    }

    return false;
}

</script>

</head>

<body>

<form onsubmit="return makeText();">
<p><label for="text">Enter your text</label> <input type="text" name="text" id="text" size="20"></p>
<p><label for="pattern">Choose a pattern</label> <select id="pattern"><option value="leaves">Leaves</option><option value="lights">Lights</option></select></p>
<p><input type="submit" value="Make!"></p>
</form>

<div id="textArea"></div>

</body>
</html>

and you can also see it in action.

alt text < this is one of the letter cutouts (difficult to see!)

alt text and one of the background patterns.

I got bored at G, so you can only write words that contain the letters a-g, it's also a little lazy in that it only does upper case and there are only 2 patterns but hopefully it should be enough to give you an idea

MalphasWats
Yep, this is totally what I was just writing up using a negative mask. This is the most flexible approach, you can easily generate a list of the patterns by reading a directory list. If you need full png compatibility in IE6 use dd_belated http://www.dillerdesign.com/experiment/DD_belatedPNG/
Tom
You could even save the mask letters as one large image and use it as a sprite. So each letter then would be a DIV with the background image moved the the correct position. That'd make it even easier if you wanted to create a few masks via different fonts. All that said, it'd not perfect, as your image based letters will end up being monospaced. I'd maybe look at using Flash for this.
DA
+1 for elegant solution and even taking the long road of putting up a sample - bravo!
Till
+1  A: 

Even on the server this is not a trivial issue, as it is not possible to do a direct Fill() using a color mask, which is esentially what you need.

I would sooner loop over the existing pixels, do a lookup and write the pattern pixel to the resulting image if the letter image is non-white:

Graphics g = Graphics.FromImage(letterImg);
Brush brush = new System.Drawing.TextureBrush(patternImg);
for (int x=0;i<letterImg.Width;i++)
  for (int y=0;y<letterImg.Height;y++)
{
    Color mask = img.GetPixel(x,y);
    //Color pattern = patternImg.GetPixel(x % patternImg.Width, y % patternImg.Height);
    if (mask != Color.White)
{   
     g.FillRectagle(brush,x,y,1,1);
}

}

And then find a naming scheme to cache these on the disk after creation so you only need to render each pattern<->letter combination once.

Radu094
+1  A: 

GDI+ works fine under ASP.NET. (WPF used to, but then was broken with a service pack. Not sure if it's working again now).

Building images server-side is pretty easy - the trick is linking them to client-side requests. I made use of a stack representation - my article here:

http://www.hackification.com/2008/10/29/stack-based-processing-part-2/

stusmith
+1  A: 

After confirming with OO (the poster) I wanted to throw Flash in the mix.

Flash has the advantage that it has really nice anti-aliasing and awesome typography support (especially since CS4). You could choose to have the entire interface in Flash where you type the patterned letters directly or just have the swf render the result according to parameters specifying the name and the pattern.

Personally I would make the whole thing in Flash, supplying the interface with the optional patterns with for instance JSON. But the most basic steps you could take are

create a form with input for the name and a select box for the patterns

display the flash file with parameters patterntype.swf?name=flash&pattern=disco.png

The basic concept is having the Text set as the mask for the pattern. I did a quick test in flash to test the performance of tiling the pattern (and if it would actually work)

package {

    import flash.display.MovieClip;
    import flash.display.Loader;
    import flash.net.URLRequest;
    import flash.events.Event;
    import flash.display.Sprite;
    import flash.display.Bitmap;
    import flash.display.BitmapData;


    public class PatternType extends MovieClip {
     /* library element, use the textfield to change the font */
     public var nameDisplay:NameDisplay;

     private var loader:Loader;

            /* parameters, you can pass these as request parameters, or make a javascript call to get/set them */
     private var pattern:String = "pattern.png"; 
     private var nameString:String = "Heidi Klum";
     private var container:MovieClip;


     public function PatternType() {
      container = new MovieClip();
      addChild(container);

      nameDisplay = new NameDisplay();
      nameDisplay.txt.text = nameString;
      nameDisplay.cacheAsBitmap = true; //mask wont work without this.
      container.addChild(nameDisplay)

      container.mask = nameDisplay;

      loader = new Loader();
      loader.contentLoaderInfo.addEventListener(Event.COMPLETE, handlePatternLoaded);
      loader.load(new URLRequest(pattern));
     }


     private function handlePatternLoaded(e:Event) {
      trace("complete");
      var bmp:Bitmap = e.target.content as Bitmap;

      var rows:int = Math.ceil(stage.stageHeight / bmp.height);
      var cols:int = Math.ceil(stage.stageWidth / bmp.width);

      //tile the texture
      for(var i:int = 0; i < cols; i++) {
       for(var j:Number = 0; j < rows; j++) {
        var b:Bitmap = new Bitmap(bmp.bitmapData.clone());
        b.x = i * bmp.width;
        b.y = j * bmp.height;
        container.addChild(b);
       }
      }
     }

    }
}

When everything is to the users wishes, you can create a new Bitmap object from the result and send the data to the server which can write the data directly to a bitmap file (jpeg is also possible).

A few things that could sell Flash a bit more:

  • you can rotate the text
  • use exotic fonts
  • use different patterns per letter
  • scale the text
  • use custom made image filters
  • move in 3d space (x,y,z translation rotation, perspective)
  • Save the result as an image
Les
Flash is rarely my first choice, but for this particular task, it's probably the most ideal option.
DA