1 // TODO: RENDERING NEEDS CLEANING!! //
  2 
  3 /**
  4  * The Render Engine
  5  * BitmapText
  6  *
  7  * @fileoverview A bitmap font renderer for render contexts that don't
  8  *               support fonts natively.
  9  *
 10  * @author: Brett Fattori (brettf@renderengine.com)
 11  * @author: $Author: bfattori $
 12  * @version: $Revision: 1555 $
 13  *
 14  * Copyright (c) 2011 Brett Fattori (brettf@renderengine.com)
 15  *
 16  * Permission is hereby granted, free of charge, to any person obtaining a copy
 17  * of this software and associated documentation files (the "Software"), to deal
 18  * in the Software without restriction, including without limitation the rights
 19  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 20  * copies of the Software, and to permit persons to whom the Software is
 21  * furnished to do so, subject to the following conditions:
 22  *
 23  * The above copyright notice and this permission notice shall be included in
 24  * all copies or substantial portions of the Software.
 25  *
 26  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 27  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 28  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 29  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 30  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 31  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 32  * THE SOFTWARE.
 33  *
 34  */
 35 
 36 // The class this file defines and its required classes
 37 R.Engine.define({
 38     "class":"R.text.BitmapText",
 39     "requires":[
 40         "R.math.Math2D",
 41         "R.util.RenderUtil",
 42         "R.text.AbstractTextRenderer"
 43     ]
 44 });
 45 
 46 /**
 47  * @class A text renderer which draws text from a bitmap font file.  This type of text
 48  *        renderer is only supported by the {@link R.rendercontexts.CanvasContext}.  For an {@link R.rendercontexts.HTMLElementContext}
 49  *        or a derivative, use the {@link R.text.ContextText} renderer.
 50  *
 51  * @constructor
 52  * @param font {Font} A resource obtained by calling {@link FontResourceLoader#get}
 53  * @extends R.text.AbstractTextRenderer
 54  * @see R.resources.loaders.BitmapFontLoader
 55  */
 56 R.text.BitmapText = function () {
 57     return R.text.AbstractTextRenderer.extend(/** @scope R.text.BitmapText.prototype */{
 58 
 59         font:null,
 60         spacing:0,
 61 
 62         /** @private */
 63         constructor:function (font) {
 64             this.base();
 65             this.font = font;
 66         },
 67 
 68         /**
 69          * Release the text renderer back into the pool for reuse
 70          */
 71         release:function () {
 72             this.base();
 73             this.font = null;
 74             this.spacing = 0;
 75         },
 76 
 77         /**
 78          * Calculate the bounding box for the text and set it on the host object.
 79          * @private
 80          */
 81         calculateBoundingBox:function () {
 82             var text = this.getText(), lCount = text.length, align = this.getTextAlignment(),
 83                 letter = (align == R.text.AbstractTextRenderer.ALIGN_RIGHT ? text.length - 1 : 0),
 84                 kern = (align == R.text.AbstractTextRenderer.ALIGN_RIGHT ? -this.font.info.kerning : this.font.info.kerning),
 85                 space = R.math.Point2D.create((align == R.text.AbstractTextRenderer.ALIGN_RIGHT ? -this.font.info.space : this.font.info.space), 0),
 86                 cW, cH = this.font.info.height, cS = 0, y = 0, pc = R.math.Point2D.create(0, 0);
 87 
 88             // Run the text to get its bounding box
 89             var weight = this.getTextWeight();
 90             for (var wT = 0; wT < weight; wT++) {
 91 
 92                 pc.set(wT * 0.5, 0);
 93 
 94                 // 1st pass: The text
 95                 letter = (align == R.text.AbstractTextRenderer.ALIGN_RIGHT ? text.length - 1 : 0);
 96                 lCount = text.length;
 97 
 98                 while (lCount-- > 0) {
 99                     var chr = text.charCodeAt(letter);
100                     if (chr == 10) {
101                         y += (cH * this.getSize()) + this.getLineSpacing();
102                         pc.set(0, y);
103                     }
104                     else {
105                         var glyph = chr - 32;
106                         if (glyph == 0) {
107                             // A space
108                             pc.add(space);
109                         }
110                         else {
111                             // Draw the text
112                             cS = this.font.info.letters[glyph - 1];
113                             cW = this.font.info.letters[glyph] - cS;
114                             pc.add(R.math.Point2D.create(cW, 0).mul(kern));
115                         }
116                     }
117 
118                     letter += (align == R.text.AbstractTextRenderer.ALIGN_RIGHT ? -1 : 1);
119                 }
120             }
121 
122             // Set the bounding box
123             this.getGameObject().getBoundingBox().set(0, 0, pc.x * this.getSize(), cH * this.getSize());
124             pc.destroy();
125         },
126 
127         /**
128          * Set the scaling of the text
129          * @param size {Number}
130          */
131         setSize:function (size) {
132             this.base(size);
133             this.generated = false;
134             this.calculateBoundingBox();
135         },
136 
137         /**
138          * Set the text to render.
139          *
140          * @param text {String} The text to render
141          */
142         setText:function (text) {
143             // If the font only supports uppercase letters
144             text = (this.font.upperCaseOnly ? String(text).toUpperCase() : text);
145 
146             // Replace special chars
147             this.base(text);
148             this.generated = false;
149             this.calculateBoundingBox();
150         },
151 
152         /**
153          * @private
154          */
155         execute:function (renderContext, time, dt) {
156 
157             if (this.getText().length == 0) {
158                 return;
159             }
160 
161             renderContext.pushTransform();
162             renderContext.setScale(this.getSize());
163 
164             var text = this.getText(), lCount = text.length, align = this.getTextAlignment(),
165                 letter = (align == R.text.AbstractTextRenderer.ALIGN_RIGHT ? text.length - 1 : 0),
166                 kern = (align == R.text.AbstractTextRenderer.ALIGN_RIGHT ? -this.font.info.kerning : this.font.info.kerning),
167                 space = R.math.Point2D.create((align == R.text.AbstractTextRenderer.ALIGN_RIGHT ? -this.font.info.space : this.font.info.space), 0),
168                 cW, cH = this.font.info.height, cS = 0, y = 0, lineCount = 1;
169 
170             // Render the text
171             var weight = this.getTextWeight();
172             for (var wT = 0; wT < weight; wT++) {
173 
174                 var pc = R.math.Point2D.create(wT * 0.5, 0);
175 
176                 // 1st pass: The text
177                 letter = (align == R.text.AbstractTextRenderer.ALIGN_RIGHT ? text.length - 1 : 0);
178                 lCount = text.length;
179 
180                 if (renderContext.get2DContext) {
181                     renderContext.get2DContext().globalCompositeOperation = "source-over";
182                 }
183 
184                 while (lCount-- > 0) {
185                     var chr = text.charCodeAt(letter);
186                     if (chr == 10) {
187                         y += (cH * this.getSize()) + this.getLineSpacing();
188                         pc.set(0, y);
189                         lineCount++;
190                     }
191                     else {
192                         var glyph = chr - 32;
193                         if (glyph == 0) {
194                             // A space
195                             pc.add(space);
196                         }
197                         else {
198                             // Draw the text
199                             cS = this.font.info.letters[glyph - 1];
200                             cW = this.font.info.letters[glyph] - cS;
201                             var sRect = R.math.Rectangle2D.create(cS, 0, cW, cH);
202                             var rect = R.math.Rectangle2D.create(pc.x, pc.y, cW, cH);
203                             renderContext.drawImage(rect, this.font.image, sRect, this.getGameObject());
204                             pc.add(R.math.Point2D.create(cW, 0).mul(kern));
205                         }
206                     }
207 
208                     letter += (align == R.text.AbstractTextRenderer.ALIGN_RIGHT ? -1 : 1);
209                 }
210             }
211 
212             // 2nd pass: The color
213             if (renderContext.get2DContext) {
214                 renderContext.get2DContext().globalCompositeOperation = "source-atop";
215                 var r = R.math.Rectangle2D.create(0, 0, pc.x, cH * (lineCount + this.getLineSpacing()));
216                 renderContext.setFillStyle(this.getColor());
217                 renderContext.drawFilledRectangle(r);
218                 // Reset the composition operation
219                 renderContext.get2DContext().globalCompositeOperation = "source-over";
220                 r.destroy();
221             }
222 
223             pc.destroy();
224             space.destroy();
225 
226             renderContext.popTransform();
227         }
228     }, /** @scope R.text.BitmapText.prototype */ {
229         /**
230          * Get the class name of this object
231          * @return {String} The string "R.text.BitmapText"
232          */
233         getClassName:function () {
234             return "R.text.BitmapText";
235         }
236     });
237 
238 };