jamil
jamil

Reputation: 2437

How to convert Text into Emoji?

I have a plan to convert Text into Emoji. But, I don't know how to start. Here are some screen shots for what I am looking for.

enter image description here enter image description here

I have the idea in my mind that to achieve the above result we should save dictionary for each character but the question is how that dictionary will save the emoji according to character structure.

Upvotes: 2

Views: 1482

Answers (1)

Flovdis
Flovdis

Reputation: 3095

I would suggest you are using a simple bitmap technique. In a first step you build a black and white bitmap with the written text in the same dimensions as you like to have the final image.

enter image description here

In a second step you "divide" this image into a raster which is e.g. 20% smaller than the final emoji character to create the overlapping effect.

enter image description here

In each raster rectangle you calculate the black pixels. There you get a percentage between 0-100% black pixels. If this percentage e.g is over 40%, a random emoji is placed at the center of this rectangle.

You can improve the effect by adding a small amount of random displacement.

Final Thoughts

I implemented this and it worked great. I could improve the image even further by some small optimization:

  • For each rectangle with >40% black, I divide this rectangle into four zones (top left, top right, bottom left, bottom right).
  • If one of this zones has a black pixel in it, I count the zone as 1 otherwise as 0.
  • This creates 16 different patterns. For a few of these patterns I move displace the emoji even more from the center of the rectangle.

For example if I get 1000, I move it to the top left. If i get 1010 to the left.

Example Code

The following example code demonstrates the shown approach, without the tweaks in the "final thought" section. It is written in Swift for a Cocoa application. You see the application delegate of a simple application which provides a text field and an image view in the main window.

You can complete the application adding the text field and image view to the main window and bind the values to the outlets textField and imageView. Also you have to bind the text field value to the parameterText property.

The output of the application looks like this:

enter image description here

@NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate {

    @IBOutlet weak var window: NSWindow!
    @IBOutlet weak var textField: NSTextField!
    @IBOutlet weak var imageView: NSImageView!

    var updateTimer: NSTimer = NSTimer()

    let canvasSize = CGSize(width: 1024, height: 768)
    let canvasPadding: CGFloat = 32.0
    let emojiSize = 20.0
    let emojiSet: [NSString] = ["🌸","🌺","🌼","🍑"]
    let emojiJitter = 4.0

    var parameterText: NSString = "Hello!" {
        didSet {
            triggerImageUpdate()
        }
    }

    func applicationDidFinishLaunching(aNotification: NSNotification) {
        triggerImageUpdate()
    }

    func applicationWillTerminate(aNotification: NSNotification) {
    }

    func triggerImageUpdate() {
        updateTimer.invalidate()
        updateTimer = NSTimer.scheduledTimerWithTimeInterval(0.5, target: self, selector: Selector("updateImage"), userInfo: nil, repeats: false);
    }

    func updateImage() {
        imageView.image = createEmojiImage(parameterText, size: canvasSize, padding: canvasPadding)
    }

    // This function creates a simple bitmap with the given text. The text
    // is centered in the bitmap and scaled to the maximum size.
    func createTextImage(text: NSString, size: CGSize, padding: CGFloat) -> NSImage {
        let canvasRect = CGRect(x: 0.0, y: 0.0, width: size.width, height: size.height)
        let textRect = CGRectInset(canvasRect, padding, padding)
        var textBitmap = NSImage(size: size)
        textBitmap.lockFocus()
        NSColor.whiteColor().setFill()
        NSRectFill(canvasRect)
        let textFont = NSFont(name: "Helvetica Bold", size: 100)
        var textAttributes: [NSObject: AnyObject] = [
            NSFontAttributeName: textFont!,
            NSForegroundColorAttributeName: NSColor.blackColor()]
        let textSize = text.sizeWithAttributes(textAttributes);
        let scaleWidth = textRect.size.width/textSize.width
        let scaleHeight = textRect.size.height/textSize.height
        let scale: CGFloat
        if (scaleWidth < scaleHeight) {
            scale = scaleWidth
        } else {
            scale = scaleHeight
        }
        let scaledCanvasSize = CGSize(width: size.width/scale, height: size.height/scale)
        let offset = CGPoint(x: (scaledCanvasSize.width - textSize.width)/2.0,
            y: (scaledCanvasSize.height - textSize.height)/2.0)
        CGContextScaleCTM(NSGraphicsContext.currentContext()!.CGContext, scale, scale)
        text.drawAtPoint(offset, withAttributes: textAttributes)
        textBitmap.unlockFocus()
        return textBitmap
    }


    func createTextBitmap(text: NSString, size: CGSize, padding: CGFloat) -> NSBitmapImageRep {
        let textImage = createTextImage(text, size: size, padding: padding)
        let tiffImageData = textImage.TIFFRepresentation
        return NSBitmapImageRep(data: tiffImageData!)!
    }


    // This is a class which represents a single field.
    class Field {
        let center: CGPoint
        let black: Double
        init(center: CGPoint, black: Double) {
            self.center = center
            self.black = black
        }
    }

    // A function to get the black value from a certain rectangle in an image.
    func blackValue(image: NSBitmapImageRep, rect: CGRect) -> Double {
        let pixelInRect = Int(rect.size.width * rect.size.height)
        var blackCount = 0;
        for (var x = 0; x < Int(rect.size.width); ++x) {
            for (var y = 0; y < Int(rect.size.height); ++y) {
                var color = image.colorAtX(Int(rect.origin.x), y: Int(rect.origin.y))!
                if (color.redComponent < 0.1) {
                    blackCount++
                }
            }
        }
        return Double(blackCount) / Double(pixelInRect)
    }

    // A function to rasterize the bitmap into single fields.
    func rasterizeBitmap(image: NSBitmapImageRep, size: CGFloat) -> (width: Int, fields: [Field]) {
        let width = Int(image.size.width/size)
        let height = Int(image.size.height/size)
        var fields = [Field]()
        for (var x = 0; x < width; ++x) {
            for (var y = 0; y < height; ++y) {
                let rect = CGRect(x: CGFloat(x) * size, y: CGFloat(y) * size, width: size, height: size)
                let center = CGPoint(x: floor(CGFloat(x) * size + size/2.0), y: image.size.height - floor(CGFloat(y) * size + size/2.0))
                let black = blackValue(image, rect: rect)
                var field = Field(center: center, black: black)
                fields.append(field)
            }
        }
        return (width, fields)
    }

    // Function to create the emoji image.
    func createEmojiImage(text: NSString, size: CGSize, padding: CGFloat) -> NSImage {
        // Create and rasterize the bitmap.
        let textBitmap = self.createTextBitmap(text, size: size, padding: padding)
        let (fieldsWidth, fields) = self.rasterizeBitmap(textBitmap, size: CGFloat(emojiSize*0.75))
        // Create a new image
        var result = NSImage(size: size)
        result.lockFocus()
        NSColor.whiteColor().setFill()
        let canvasRect = CGRect(x: 0.0, y: 0.0, width: size.width, height: size.height)
        NSRectFill(canvasRect)
        let textFont = NSFont(name: "Apple Color Emoji", size: CGFloat(self.emojiSize))
        var textAttributes: [NSObject: AnyObject] = [NSFontAttributeName: textFont!]
        NSColor.blackColor().setFill()
        for field in fields {
            let jitterX = CGFloat(Double(arc4random_uniform(101))/100.0*self.emojiJitter-self.emojiJitter/2.0)
            let jitterY = CGFloat(Double(arc4random_uniform(101))/100.0*self.emojiJitter-self.emojiJitter/2.0)
            if field.black > 0.4 {
                var randomEmoji = self.emojiSet[Int(arc4random_uniform(UInt32(self.emojiSet.count)))]
                var drawingPoint = CGPoint(x: field.center.x - CGFloat(self.emojiSize/2.0) + jitterX, y: field.center.y - CGFloat(self.emojiSize/2.0) + jitterY)
                randomEmoji.drawAtPoint(drawingPoint, withAttributes: textAttributes)
            }
        }
        result.unlockFocus()
        return result
    }
}

Upvotes: 4

Related Questions