1 var jaws = (function(jaws) { 2 /** 3 * @class jaws.PixelMap 4 * @constructor 5 * 6 * Worms-style terrain collision detection. Created from a normal image. 7 * Read out specific pixels. Modify as you would do with a canvas. 8 * 9 * @property {string} image the image of the terrain 10 * @property {int} scale_image Scale the image by this factor 11 * 12 * @example 13 * tile_map = new jaws.Parallax({image: "map.png", scale_image: 4}) // scale_image: 4 for retro blocky feeling! 14 * tile_map.draw() // draw on canvas 15 * tile_map.nameColor([0,0,0,255], "ground") // give the color black the name "ground" 16 * tile_map.namedColorAtRect("ground", player.rect()) // True if players boundingbox is touching any black pixels on tile_map 17 * 18 */ 19 jaws.PixelMap = function PixelMap(options) { 20 if( !(this instanceof arguments.callee) ) return new arguments.callee( options ); 21 22 this.options = options 23 this.scale = options.scale || 1 24 this.x = options.x || 0 25 this.y = options.y || 0 26 27 if(options.image) { 28 this.setContext(options.image); 29 30 if(options.scale_image) { 31 this.setContext( jaws.retroScaleImage(this.context.canvas, options.scale_image) ) 32 } 33 34 this.width = this.context.canvas.width * this.scale; 35 this.height = this.context.canvas.height * this.scale; 36 } 37 else { jaws.log.warn("PixelMap needs an image to work with") } 38 39 this.named_colors = []; 40 this.update(); 41 } 42 43 /* 44 * Initiates a drawable context from given image. 45 * @private 46 */ 47 jaws.PixelMap.prototype.setContext = function(image) { 48 var image = jaws.isDrawable(image) ? image : jaws.assets.get(image) 49 this.context = jaws.imageToCanvasContext(image) 50 } 51 52 /** 53 * Updates internal pixel-array from the canvas. If we modify the 'terrain' (paint on pixel_map.context) we'll need to call this method. 54 */ 55 jaws.PixelMap.prototype.update = function(x, y, width, height) { 56 if(x === undefined || x < 0) x = 0; 57 if(y === undefined || y < 0) y = 0; 58 if(width === undefined || width > this.width) width = this.width; 59 if(height === undefined || height > this.height) height = this.height; 60 61 // No arguments? Read whole canvas, replace this.data 62 if(arguments.length == 0) { 63 this.data = this.context.getImageData(x, y, width, height).data 64 } 65 // Read a rectangle from the canvas, replacing relevant pixels in this.data 66 else { 67 var tmp = this.context.getImageData(x, y, width, height).data 68 var tmp_count = 0; 69 70 // Some precalculation-optimizations 71 var one_line_down = this.width * 4; 72 var offset = (y * this.width * 4) + (x*4); 73 var horizontal_line = width*4; 74 75 for(var y2 = 0; y2 < height; y2++) { 76 for(var x2 = 0; x2 < horizontal_line; x2++) { 77 this.data[offset + x2] = tmp[tmp_count++]; 78 } 79 offset += one_line_down; 80 } 81 } 82 } 83 84 /** 85 * Draws the pixel map on the maincanvas 86 */ 87 jaws.PixelMap.prototype.draw = function() { 88 jaws.context.drawImage(this.context.canvas, this.x, this.y, this.width, this.height) 89 } 90 91 /** 92 * Trace the outline of a Rect until a named color found. 93 * 94 * @param {object} Rect Instance of jaws.Rect() 95 * @param {string} Color_Filter Only look for this named color 96 * 97 * @return {string} name of found color 98 */ 99 jaws.PixelMap.prototype.namedColorAtRect = function(rect, color) { 100 var x = rect.x 101 var y = rect.y 102 103 for(; x < rect.right-1; x++) if(this.namedColorAt(x, y) == color || color===undefined) return this.namedColorAt(x,y); 104 for(; y < rect.bottom-1; y++) if(this.namedColorAt(x, y) == color || color===undefined) return this.namedColorAt(x,y); 105 for(; x > rect.x; x--) if(this.namedColorAt(x, y) == color || color===undefined) return this.namedColorAt(x,y); 106 for(; y > rect.y; y--) if(this.namedColorAt(x, y) == color || color===undefined) return this.namedColorAt(x,y); 107 108 return false; 109 } 110 111 /** 112 * Read current color at given coordinates X/Y 113 * 114 * @return {array} 4 integers [R, G, B, A] representing the pixel at x/y 115 */ 116 jaws.PixelMap.prototype.at = function(x, y) { 117 x = parseInt(x) 118 y = parseInt(y) 119 if(y < 0) y = 0; 120 121 var start = (y * this.width * 4) + (x*4); 122 var R = this.data[start]; 123 var G = this.data[start + 1]; 124 var B = this.data[start + 2]; 125 var A = this.data[start + 3]; 126 return [R, G, B, A]; 127 } 128 129 /** 130 * Get previously named color if it exists at given x/y-coordinates. 131 * 132 * @return {string} name or color 133 */ 134 jaws.PixelMap.prototype.namedColorAt = function(x, y) { 135 var a = this.at(x, y); 136 for(var i=0; i < this.named_colors.length; i++) { 137 var name = this.named_colors[i].name; 138 var c = this.named_colors[i].color; 139 if(c[0] == a[0] && c[1] == a[1] && c[2] == a[2] && c[3] == a[3]) return name; 140 } 141 } 142 143 /** 144 * Give a RGBA-array a name. Later on we can work with names instead of raw colorvalues. 145 * 146 * @example 147 * pixel_map.nameColor([0,0,0,255], "ground") // Give the color black (with no transparency) the name "ground" 148 */ 149 jaws.PixelMap.prototype.nameColor = function(color, name) { 150 this.named_colors.push({name: name, color: color}); 151 } 152 153 return jaws; 154 })(jaws || {}); 155