Circles Mosaic
Creates a mosaic with circles that don't overlaps each other
/**
* Copyright will_costa ( http://wonderfl.net/user/will_costa )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/2HZz
*/
package {
import com.adobe.images.JPGEncoder;
import com.bit101.components.PushButton;
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.Sprite;
import flash.display.StageAlign;
import flash.display.StageScaleMode;
import flash.events.Event;
import flash.net.FileReference;
import flash.utils.ByteArray;
/**
* @author Will Costa
* @author http://www.willcosta.net
*/
[SWF(backgroundColor="#000000", frameRate="60", width="465", height="465")]
public class CirclesMosaic extends Sprite {
public static const ITERATIONS_PER_FRAME : int = 20;
private var _source : BitmapData;
private var _container : Bitmap;
private var _mosaic : Mosaic;
private var _fileLoader:FileLoader;
private var _loadButton:PushButton;
private var _saveButton:PushButton;
private var _initialized:Boolean = false;
public function CirclesMosaic() {
Wonderfl.capture_delay(20);
stage.scaleMode = StageScaleMode.NO_SCALE;
stage.align = StageAlign.TOP_LEFT;
_fileLoader = new FileLoader();
_fileLoader.addEventListener(Event.INIT, _fileLoader_onCompleteHandler);
_loadButton = new PushButton(this, 0, 0, "Load Image", _loadButton_onClickHandler);
_saveButton = new PushButton(this, _loadButton.width + 5, 0, "Save Image", _saveButton_onClickHandler);
_saveButton.enabled = false;
}
// ----------------------------------
// Private Functions
// ----------------------------------
private function init():void{
dispose();
_initialized = true;
_source = resizeBitmapData(_fileLoader.bitmapData);
_mosaic = new Mosaic(_source);
_container = new Bitmap(_mosaic.bitmapData);
_container.x = (465-_container.width)/2;
_container.y = (465-_container.height)/2;
addChildAt(_container, 0);
addEventListener(Event.ENTER_FRAME, onEnterFrameHandler);
_saveButton.enabled = true;
}
private function dispose():void{
if(!_initialized) return;
_mosaic.dispose();
removeChild(_container);
_source.dispose();
}
private function resizeBitmapData(bitmapData : BitmapData) : BitmapData {
var bmp : Bitmap = new Bitmap(bitmapData);
if(bmp.width > 465){
bmp.width = 465;
bmp.scaleY = bmp.scaleX;
}
if(bmp.height > 465){
bmp.height = 465;
bmp.scaleX = bmp.scaleY;
}
var resizedBD:BitmapData = new BitmapData(bmp.width, bmp.height);
resizedBD.draw(bmp,bmp.transform.matrix);
return resizedBD;
}
// ----------------------------------
// Event Handlers
// ----------------------------------
private function _loadButton_onClickHandler(event:Event):void{
_fileLoader.load();
}
private function _saveButton_onClickHandler(event : Event):void {
var myBitmapData : BitmapData = new BitmapData(_container.width, _container.height);
myBitmapData.draw(_container);
var jpgEncoder:JPGEncoder = new JPGEncoder(80);
var imgByteData:ByteArray = jpgEncoder.encode(myBitmapData);
var file:FileReference = new FileReference();
file.save(imgByteData, "image_mosaic.jpg");
}
private function _fileLoader_onCompleteHandler(event : Event) : void {
init();
}
private function onEnterFrameHandler(event : Event) : void {
for (var i : int = 0; i < ITERATIONS_PER_FRAME; i++) {
_mosaic.expandPixel(int(Math.random() * _mosaic.width), int(Math.random() * _mosaic.height));
}
}
}
}
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.Loader;
import flash.display.Shape;
import flash.events.Event;
import flash.events.EventDispatcher;
import flash.geom.Point;
import flash.net.FileFilter;
import flash.net.FileReference;
internal class Mosaic {
public static const IMAGE_DEPTH : int = 6;
public static const BASE_COLOR:uint = 0xFF00FF00;
public var width : Number = 0;
public var height : Number = 0;
private var _originalSource : BitmapData;
private var _source : BitmapData;
private var _filledArea : BitmapData;
private var _bitmapData : BitmapData;
private var _circle : Shape;
public function Mosaic(source : BitmapData) {
width = source.width;
height = source.height;
_circle = new Shape();
_source = source;
_originalSource = _source.clone();
changeColorDepth(_source, IMAGE_DEPTH);
_bitmapData = new BitmapData(width, height, false, 0);
_filledArea = new BitmapData(width, height, true, BASE_COLOR);
}
public function expandPixel(x : int, y : int):void {
var checkColor : uint = _filledArea.getPixel32(x, y);
if(checkColor != BASE_COLOR) return;
var color : uint = _source.getPixel32(x, y);
var fillColor : uint = _originalSource.getPixel(x, y);
var strokeColor : uint = darkenColor(fillColor);
var area : BitmapData = _source.clone();
area.threshold(area, area.rect, new Point(), "==", color,0);
var size:int = 1;
while(_circle.width < _source.width){
_circle.graphics.clear();
_circle.graphics.lineStyle(1, 0);
_circle.graphics.beginFill(fillColor);
_circle.graphics.drawCircle(size, size, size);
_circle.graphics.endFill();
_circle.graphics.lineStyle();
_circle.graphics.beginFill(strokeColor);
if(size > 5)_circle.graphics.drawCircle(size, size, size/2);
_circle.graphics.endFill();
var hit : BitmapData = new BitmapData(_circle.width, _circle.height, true, 0);
hit.draw(_circle);
if(hit.hitTest(new Point(x-size, y-size), 255, area, new Point(0, 0))) {
_circle.x = x - size;
_circle.y = y - size;
_source.draw(_circle, _circle.transform.matrix);
_bitmapData.draw(_circle, _circle.transform.matrix);
_filledArea.draw(_circle, _circle.transform.matrix);
break;
}
hit.dispose();
size += 1;
}
area.dispose();
}
public function dispose():void {
_bitmapData.dispose();
_filledArea.dispose();
_source.dispose();
_originalSource.dispose();
}
private function changeColorDepth(source : BitmapData, depth : int = 16) : void {
var Ra : Array = new Array(256);
var Ga : Array = new Array(256);
var Ba : Array = new Array(256);
var n : Number = 256 / ( depth / 3 );
for (var i : int = 0;i < 256;i++) {
Ba[i] = Math.floor(i / n) * n;
Ga[i] = Ba[i] << 8;
Ra[i] = Ga[i] << 8;
}
source.paletteMap(source, source.rect, new Point(), Ra, Ga, Ba);
}
private function darkenColor(color : uint) : uint {
var q : Number = .9;
var colorA : uint = (color >> 24) & 0xFF;
var colorR : uint = (color >> 16) & 0xFF;
var colorG : uint = (color >> 8) & 0xFF;
var colorB : uint = color & 0xFF;
var resultA : uint = colorA * q;
var resultR : uint = colorR * q;
var resultG : uint = colorG * q;
var resultB : uint = colorB * q;
var resultColor : uint = resultA << 24 | resultR << 16 | resultG << 8 | resultB;
return resultColor;
}
public function get bitmapData() : BitmapData {
return _bitmapData;
}
}
internal class FileLoader extends EventDispatcher {
public var data : *;
public var bitmapData : BitmapData;
private var typeFilter : Array;
private var file : FileReference;
private var loader : Loader;
public function FileLoader() {
init();
}
private function init():void {
file = new FileReference();
file.addEventListener(Event.SELECT, select, false, 0, true);
file.addEventListener(Event.CANCEL, cancel, false, 0, true);
file.addEventListener(Event.COMPLETE, complete, false, 0, true);
var fileFilter : FileFilter = new FileFilter("Images", "*.jpg;*.jpeg;*.gif;*.png");
typeFilter = [fileFilter];
loader = new Loader();
loader.contentLoaderInfo.addEventListener(Event.INIT, initialize, false, 0, true);
}
public function load():void {
file.browse(typeFilter);
}
private function select(evt : Event):void {
file.load();
dispatchEvent(evt);
}
private function cancel(evt : Event):void {
dispatchEvent(evt);
}
private function complete(evt : Event):void {
data = evt.target.data;
loader.loadBytes(data);
dispatchEvent(evt);
}
private function initialize(evt : Event):void {
bitmapData = null;
var bitmap : Bitmap = Bitmap(evt.target.content);
bitmapData = bitmap.bitmapData;
dispatchEvent(evt);
}
}