1 /** 2 * The Render Engine 3 * RenderUtil 4 * 5 * @fileoverview A static class with helper methods for rendering screen shots, partial images, and some effects. 6 * 7 * @author: Brett Fattori (brettf@renderengine.com) 8 * @author: $Author: bfattori@gmail.com $ 9 * @version: $Revision: 1557 $ 10 * 11 * Copyright (c) 2011 Brett Fattori (brettf@renderengine.com) 12 * 13 * Permission is hereby granted, free of charge, to any person obtaining a copy 14 * of this software and associated documentation files (the "Software"), to deal 15 * in the Software without restriction, including without limitation the rights 16 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 17 * copies of the Software, and to permit persons to whom the Software is 18 * furnished to do so, subject to the following conditions: 19 * 20 * The above copyright notice and this permission notice shall be included in 21 * all copies or substantial portions of the Software. 22 * 23 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 24 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 25 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 26 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 27 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 28 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 29 * THE SOFTWARE. 30 * 31 */ 32 33 // The class this file defines and its required classes 34 R.Engine.define({ 35 "class":"R.util.RenderUtil", 36 "requires":[ 37 "R.rendercontexts.CanvasContext", 38 "R.math.Point2D", 39 "R.math.Rectangle2D" 40 ] 41 }); 42 43 /** 44 * @class A static class of rendering utilities. Most of the methods are intended 45 * for bitmap contexts, such as canvas, but may apply to others. The methods 46 * have been designed with canvas in mind. 47 * 48 * @static 49 */ 50 R.util.RenderUtil = /** @scope R.util.RenderUtil.prototype */ { 51 52 // Private cache of temporary contexts 53 tempContexts:{}, 54 55 /** 56 * Get a temporary context to render into. Only one context will ever be created for the type 57 * specified, and cached for repeated use. It will be cleaned up when the engine is shut down. 58 * @param type {R.rendercontexts.RenderContext2D} The context class to mimic 59 * @param width {Number} The width of the temporary context 60 * @param height {Number} The height of the temporary context 61 * @return {R.rendercontexts.RenderContext2D} 62 */ 63 getTempContext:function (type, width, height) { 64 if (R.util.RenderUtil.tempContexts[type.getClassName()] == null) { 65 // Create the temporary context to render to 66 R.util.RenderUtil.tempContexts[type.getClassName()] = type.create("tempCtx", 800, 800); 67 68 // When the engine shuts down, clean up the contexts 69 R.Engine.onShutdown(function () { 70 for (var c in R.util.RenderUtil.tempContexts) { 71 R.util.RenderUtil.tempContexts[c].destroy(); 72 R.util.RenderUtil.tempContexts = {}; 73 } 74 }) 75 } 76 77 // Prepare the temporary context 78 var ctx = R.util.RenderUtil.tempContexts[type.getClassName()]; 79 ctx.getElement().width = width; 80 ctx.getElement().height = height; 81 ctx.reset(); 82 83 return ctx; 84 }, 85 86 /** 87 * Perform a single execution of a rendering component. 88 * @param contextType {R.rendercontexts.RenderContext2D} The type of context to render to 89 * @param renderComponent {R.components.Render} The component to render 90 * @param width {Number} The width of the temporary context 91 * @param height {Number} The height of the temporary context 92 * @param time {Number} The time in milliseconds, or <code>null</code> to use the current engine time 93 * @param offset {R.math.Point2D} The offset for the rendering position 94 * @return {String} The data URL of the rendered image 95 */ 96 renderComponentToImage:function (contextType, renderComponent, width, height, time, offset) { 97 // Get the temporary context 98 var ctx = R.util.RenderUtil.getTempContext(contextType, width, height); 99 100 time = time || R.Engine.worldTime; 101 102 // The position to render to in the context 103 offset = offset || Point2D.ZERO; 104 105 // Render the component 106 var p = R.math.Point2D.create(0, 0); 107 p.add(offset); 108 ctx.setPosition(p); 109 p.destroy(); 110 renderComponent.execute(ctx, time, 1); 111 112 // Extract the rendered image 113 return ctx.getDataURL(); 114 }, 115 116 /** 117 * Takes a screen shot of the context provided, optionally cropped to specific dimensions. 118 * @param renderContext {R.rendercontexts.RenderContext2D} The context to get a screenshot of 119 * @param [cropRect] {R.math.Rectangle2D} Optional rectangle to crop to, or <code>null</code> for the 120 * entire context. 121 * @return {String} The data URL of the screen shot 122 */ 123 screenShot:function (renderContext, cropRect) { 124 cropRect = cropRect || renderContext.getViewport(); 125 126 // Render the screenshot to the temp context 127 var ctx = R.util.RenderUtil.getTempContext(renderContext.constructor, renderContext.getViewport().w, renderContext.getViewport().h); 128 ctx.drawImage(renderContext.getViewport(), renderContext.getSurface(), cropRect); 129 130 // Return the image data 131 return ctx.getDataURL(); 132 }, 133 134 /** 135 * Extract the image data URL from the provided image. The image can either be an HTML <img> element, 136 * or it can be another render context. This method currently only works with the canvas context. 137 * @param image {Object} Image or context 138 * @param [cropRect] {R.math.Rectangle2D} A rectangle to crop to, or <code>null</code> to use the entire image 139 * @param [contextType] {R.rendercontexts.RenderContext2D} Optional render context class, or <code>null</code> to 140 * assume a canvas context. 141 * @return {String} A data URL for the extracted image 142 */ 143 extractDataURL:function (image, cropRect, contextType) { 144 contextType = contextType || R.rendercontexts.CanvasContext; 145 var img = $(image), imgRect = R.math.Rectangle2D.create(0, 0, img.attr("width"), img.attr("height")); 146 147 if (cropRect) { 148 imgRect.sub(cropRect.getTopLeft()); 149 } 150 151 // Get the temporary context 152 var w = cropRect ? cropRect.w : imgRect.w, h = cropRect ? cropRect.h : imgRect.h, 153 ctx = R.util.RenderUtil.getTempContext(contextType, w, h); 154 155 ctx.drawImage(imgRect, image); 156 157 // Return the image data from the temp context 158 return ctx.getDataURL("image/png"); 159 }, 160 161 /** 162 * Extract the image data from the provided image. The image can either be an HTML <img> element, 163 * or it can be another render context. This method currently only works with the canvas context. 164 * @param image {Object} Image or context 165 * @param [cropRect] {R.math.Rectangle2D} A rectangle to crop to, or <code>null</code> to use the entire image 166 * @param [contextType] {R.rendercontexts.RenderContext2D} Optional render context class, or <code>null</code> to 167 * assume a canvas context. 168 * @return {Object} Image data object with "width", "height", and an Array of each pixel, represented as 169 * RGBA data where each element is represented by an integer 0-255. 170 */ 171 extractImageData:function (image, cropRect, contextType) { 172 contextType = contextType || R.rendercontexts.CanvasContext; 173 var img = $(image), w = img.attr("width"), h = img.attr("height"); 174 var imgRect = R.math.Rectangle2D.create(0, 0, w, h); 175 176 // Get the temporary context 177 var ctx = R.util.RenderUtil.getTempContext(contextType, w, h); 178 ctx.drawImage(imgRect, image); 179 180 // Return the image data from the temp context 181 return ctx.getImage(cropRect); 182 }, 183 184 blurCtx:null, 185 186 /** 187 * Blur the contents of the <tt>renderContext</tt> using the number of passes specified. 188 * The blur operation is fairly quick and is an approximation of a blur, not an actual 189 * blur filter. However, this method is slow and shouldn't be called per frame due to 190 * passing the render context on the stack. 191 * 192 * @param renderContext {R.rendercontexts.RenderContext2D} The context to blur 193 * @param [passes] {Number} Optional number of passes to apply (default: 1) 194 */ 195 blur:function (renderContext, passes) { 196 var i, x, y, rect = R.math.Rectangle2D.create(renderContext.getViewport()), 197 ctx = R.util.RenderUtil.getTempContext(renderContext.constructor, 198 renderContext.getViewport().w, 199 renderContext.getViewport().h); 200 201 // Extract the context's current image 202 ctx.drawImage(rect, renderContext.getSurface()); 203 204 // Run the blur 205 passes = passes || 1; 206 renderContext.get2DContext().globalAlpha = 0.125; 207 for (i = 1; i <= passes; i++) { 208 for (y = -1; y < 2; y++) { 209 for (x = -1; x < 2; x++) { 210 rect.x = x; 211 rect.y = y; 212 renderContext.drawImage(rect, ctx.getSurface()); 213 } 214 } 215 } 216 renderContext.get2DContext().globalAlpha = 1.0; 217 }, 218 219 /** 220 * Get a mask for the provided image. The mask is a simple pixel, no-pixel image which 221 * exactly mimics the provided image. 222 * 223 * @param image {Object} Image or context 224 * @param [cropRect] {R.math.Rectangle2D} A rectangle to crop to, or <code>null</code> to use the entire image 225 * @param [contextType] {R.rendercontexts.RenderContext2D} Optional render context class, or <code>null</code> to 226 * assume a canvas context. 227 * @return {String} A data URL for the image mask 228 */ 229 getMaskImage:function (image, cropRect, contextType) { 230 var imgData = R.util.RenderUtil.extractImageData(image, cropRect, contextType); 231 232 // Modify the image data so each pixel is either fully on or off 233 for (var pix = 0; pix < imgData.data.length; pix += 4) { 234 if (imgData.data[pix + 3] != 0) { 235 imgData.data[pix] = 255; 236 imgData.data[pix + 1] = 255; 237 imgData.data[pix + 2] = 255; 238 imgData.data[pix + 3] = 255; 239 } else { 240 imgData.data[pix] = 0; 241 imgData.data[pix + 1] = 0; 242 imgData.data[pix + 2] = 0; 243 imgData.data[pix + 3] = 0; 244 } 245 } 246 247 // Recreate the temp context and draw the new image so we can extract the data URL 248 var img = $(image), w = img.attr("width"), h = img.attr("height"); 249 var imgRect = R.math.Rectangle2D.create(0, 0, w, h); 250 251 // Get the temporary context 252 var ctx = R.util.RenderUtil.getTempContext(contextType, w, h); 253 ctx.putImage(imgData, R.math.Point2D.ZERO); 254 255 // Extract the data URL 256 return ctx.getDataURL("image/png"); 257 } 258 };