code cleanup: dont use function calls like dot_v3v3, pow and sqrt within macros which...
[blender.git] / source / blender / blenlib / intern / freetypefont.c
1 /*
2  * ***** BEGIN GPL LICENSE BLOCK *****
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software Foundation,
16  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  *
18  * The Original Code is written by Rob Haarsma (phase)
19  * All rights reserved.
20  *
21  * Contributor(s): none yet.
22  *
23  * ***** END GPL LICENSE BLOCK *****
24  *
25  * This code parses the Freetype font outline data to chains of Blender's beziertriples.
26  * Additional information can be found at the bottom of this file.
27  *
28  * Code that uses exotic character maps is present but commented out.
29  */
30
31 /** \file blender/blenlib/intern/freetypefont.c
32  *  \ingroup bli
33  */
34
35
36 #ifdef WIN32
37 #pragma warning (disable:4244)
38 #endif
39
40 #include <ft2build.h>
41 #include FT_FREETYPE_H
42 /* not needed yet */
43 // #include FT_GLYPH_H
44 // #include FT_BBOX_H
45 // #include FT_SIZES_H
46 // #include <freetype/ttnameid.h>
47
48 #include "MEM_guardedalloc.h"
49
50 #include "BLI_vfontdata.h"
51 #include "BLI_blenlib.h"
52 #include "BLI_math.h"
53 #include "BLI_utildefines.h"
54
55 //XXX #include "BIF_toolbox.h"
56
57 #include "BKE_font.h"
58
59
60 #include "DNA_vfont_types.h"
61 #include "DNA_packedFile_types.h"
62 #include "DNA_curve_types.h"
63
64 #define myMIN_ASCII     32
65 #define myMAX_ASCII     255
66
67 /* local variables */
68 static FT_Library library;
69 static FT_Error err;
70
71
72 static void freetypechar_to_vchar(FT_Face face, FT_ULong charcode, VFontData *vfd)
73 {
74         // Blender
75         struct Nurb *nu;
76         struct VChar *che;
77         struct BezTriple *bezt;
78         
79         // Freetype2
80         FT_GlyphSlot glyph;
81         FT_UInt glyph_index;
82         FT_Outline ftoutline;
83         float scale, height;
84         float dx, dy;
85         int j, k, l, m = 0;
86         
87         // adjust font size
88         height = ((double) face->bbox.yMax - (double) face->bbox.yMin);
89         if (height != 0.0f)
90                 scale = 1.0f / height;
91         else
92                 scale = 1.0f / 1000.0f;
93         
94         //      
95         // Generate the character 3D data
96         //
97         // Get the FT Glyph index and load the Glyph
98         glyph_index = FT_Get_Char_Index(face, charcode);
99         err = FT_Load_Glyph(face, glyph_index, FT_LOAD_NO_SCALE | FT_LOAD_NO_BITMAP);
100         
101         // If loading succeeded, convert the FT glyph to the internal format
102         if (!err) {
103                 int *npoints;
104                 int *onpoints;
105                 
106                 // First we create entry for the new character to the character list
107                 che = (VChar *) MEM_callocN(sizeof(struct VChar), "objfnt_char");
108                 BLI_addtail(&vfd->characters, che);
109                 
110                 // Take some data for modifying purposes
111                 glyph = face->glyph;
112                 ftoutline = glyph->outline;
113                 
114                 // Set the width and character code
115                 che->index = charcode;
116                 che->width = glyph->advance.x * scale;
117                 
118                 // Start converting the FT data
119                 npoints = (int *)MEM_callocN((ftoutline.n_contours) * sizeof(int), "endpoints");
120                 onpoints = (int *)MEM_callocN((ftoutline.n_contours) * sizeof(int), "onpoints");
121
122                 // calculate total points of each contour
123                 for (j = 0; j < ftoutline.n_contours; j++) {
124                         if (j == 0)
125                                 npoints[j] = ftoutline.contours[j] + 1;
126                         else
127                                 npoints[j] = ftoutline.contours[j] - ftoutline.contours[j - 1];
128                 }
129
130                 // get number of on-curve points for beziertriples (including conic virtual on-points)
131                 for (j = 0; j < ftoutline.n_contours; j++) {
132                         for (k = 0; k < npoints[j]; k++) {
133                                 l = (j > 0) ? (k + ftoutline.contours[j - 1] + 1) : k;
134
135                                 if (ftoutline.tags[l] == FT_Curve_Tag_On)
136                                         onpoints[j]++;
137
138                                 if (k < npoints[j] - 1) {
139                                         if (ftoutline.tags[l] == FT_Curve_Tag_Conic &&
140                                             ftoutline.tags[l + 1] == FT_Curve_Tag_Conic)
141                                         {
142                                                 onpoints[j]++;
143                                         }
144                                 }
145                         }
146                 }
147
148                 //contour loop, bezier & conic styles merged
149                 for (j = 0; j < ftoutline.n_contours; j++) {
150                         // add new curve
151                         nu  =  (Nurb *)MEM_callocN(sizeof(struct Nurb), "objfnt_nurb");
152                         bezt = (BezTriple *)MEM_callocN((onpoints[j]) * sizeof(BezTriple), "objfnt_bezt");
153                         BLI_addtail(&che->nurbsbase, nu);
154
155                         nu->type = CU_BEZIER;
156                         nu->pntsu = onpoints[j];
157                         nu->resolu = 8;
158                         nu->flag = CU_2D;
159                         nu->flagu = CU_NURB_CYCLIC;
160                         nu->bezt = bezt;
161
162                         //individual curve loop, start-end
163                         for (k = 0; k < npoints[j]; k++) {
164                                 if (j > 0) l = k + ftoutline.contours[j - 1] + 1; else l = k;
165                                 if (k == 0) m = l;
166                                         
167                                 //virtual conic on-curve points
168                                 if (k < npoints[j] - 1) {
169                                         if (ftoutline.tags[l] == FT_Curve_Tag_Conic && ftoutline.tags[l + 1] == FT_Curve_Tag_Conic) {
170                                                 dx = (ftoutline.points[l].x + ftoutline.points[l + 1].x) * scale / 2.0f;
171                                                 dy = (ftoutline.points[l].y + ftoutline.points[l + 1].y) * scale / 2.0f;
172
173                                                 //left handle
174                                                 bezt->vec[0][0] = (dx + (2 * ftoutline.points[l].x) * scale) / 3.0f;
175                                                 bezt->vec[0][1] = (dy + (2 * ftoutline.points[l].y) * scale) / 3.0f;
176
177                                                 //midpoint (virtual on-curve point)
178                                                 bezt->vec[1][0] = dx;
179                                                 bezt->vec[1][1] = dy;
180
181                                                 //right handle
182                                                 bezt->vec[2][0] = (dx + (2 * ftoutline.points[l + 1].x) * scale) / 3.0f;
183                                                 bezt->vec[2][1] = (dy + (2 * ftoutline.points[l + 1].y) * scale) / 3.0f;
184
185                                                 bezt->h1 = bezt->h2 = HD_ALIGN;
186                                                 bezt->radius = 1.0f;
187                                                 bezt++;
188                                         }
189                                 }
190
191                                 //on-curve points
192                                 if (ftoutline.tags[l] == FT_Curve_Tag_On) {
193                                         //left handle
194                                         if (k > 0) {
195                                                 if (ftoutline.tags[l - 1] == FT_Curve_Tag_Cubic) {
196                                                         bezt->vec[0][0] = ftoutline.points[l - 1].x * scale;
197                                                         bezt->vec[0][1] = ftoutline.points[l - 1].y * scale;
198                                                         bezt->h1 = HD_FREE;
199                                                 }
200                                                 else if (ftoutline.tags[l - 1] == FT_Curve_Tag_Conic) {
201                                                         bezt->vec[0][0] = (ftoutline.points[l].x + (2 * ftoutline.points[l - 1].x)) * scale / 3.0f;
202                                                         bezt->vec[0][1] = (ftoutline.points[l].y + (2 * ftoutline.points[l - 1].y)) * scale / 3.0f;
203                                                         bezt->h1 = HD_FREE;
204                                                 }
205                                                 else {
206                                                         bezt->vec[0][0] = ftoutline.points[l].x * scale - (ftoutline.points[l].x - ftoutline.points[l - 1].x) * scale / 3.0f;
207                                                         bezt->vec[0][1] = ftoutline.points[l].y * scale - (ftoutline.points[l].y - ftoutline.points[l - 1].y) * scale / 3.0f;
208                                                         bezt->h1 = HD_VECT;
209                                                 }
210                                         }
211                                         else { //first point on curve
212                                                 if (ftoutline.tags[ftoutline.contours[j]] == FT_Curve_Tag_Cubic) {
213                                                         bezt->vec[0][0] = ftoutline.points[ftoutline.contours[j]].x * scale;
214                                                         bezt->vec[0][1] = ftoutline.points[ftoutline.contours[j]].y * scale;
215                                                         bezt->h1 = HD_FREE;
216                                                 }
217                                                 else if (ftoutline.tags[ftoutline.contours[j]] == FT_Curve_Tag_Conic) {
218                                                         bezt->vec[0][0] = (ftoutline.points[l].x + (2 * ftoutline.points[ftoutline.contours[j]].x)) * scale / 3.0f;
219                                                         bezt->vec[0][1] = (ftoutline.points[l].y + (2 * ftoutline.points[ftoutline.contours[j]].y)) * scale / 3.0f;
220                                                         bezt->h1 = HD_FREE;
221                                                 }
222                                                 else {
223                                                         bezt->vec[0][0] = ftoutline.points[l].x * scale - (ftoutline.points[l].x - ftoutline.points[ftoutline.contours[j]].x) * scale / 3.0f;
224                                                         bezt->vec[0][1] = ftoutline.points[l].y * scale - (ftoutline.points[l].y - ftoutline.points[ftoutline.contours[j]].y) * scale / 3.0f;
225                                                         bezt->h1 = HD_VECT;
226                                                 }
227                                         }
228
229                                         //midpoint (on-curve point)
230                                         bezt->vec[1][0] = ftoutline.points[l].x * scale;
231                                         bezt->vec[1][1] = ftoutline.points[l].y * scale;
232
233                                         //right handle
234                                         if (k < (npoints[j] - 1)) {
235                                                 if (ftoutline.tags[l + 1] == FT_Curve_Tag_Cubic) {
236                                                         bezt->vec[2][0] = ftoutline.points[l + 1].x * scale;
237                                                         bezt->vec[2][1] = ftoutline.points[l + 1].y * scale;
238                                                         bezt->h2 = HD_FREE;
239                                                 }
240                                                 else if (ftoutline.tags[l + 1] == FT_Curve_Tag_Conic) {
241                                                         bezt->vec[2][0] = (ftoutline.points[l].x + (2 * ftoutline.points[l + 1].x)) * scale / 3.0f;
242                                                         bezt->vec[2][1] = (ftoutline.points[l].y + (2 * ftoutline.points[l + 1].y)) * scale / 3.0f;
243                                                         bezt->h2 = HD_FREE;
244                                                 }
245                                                 else {
246                                                         bezt->vec[2][0] = ftoutline.points[l].x * scale - (ftoutline.points[l].x - ftoutline.points[l + 1].x) * scale / 3.0f;
247                                                         bezt->vec[2][1] = ftoutline.points[l].y * scale - (ftoutline.points[l].y - ftoutline.points[l + 1].y) * scale / 3.0f;
248                                                         bezt->h2 = HD_VECT;
249                                                 }
250                                         }
251                                         else { //last point on curve
252                                                 if (ftoutline.tags[m] == FT_Curve_Tag_Cubic) {
253                                                         bezt->vec[2][0] = ftoutline.points[m].x * scale;
254                                                         bezt->vec[2][1] = ftoutline.points[m].y * scale;
255                                                         bezt->h2 = HD_FREE;
256                                                 }
257                                                 else if (ftoutline.tags[m] == FT_Curve_Tag_Conic) {
258                                                         bezt->vec[2][0] = (ftoutline.points[l].x + (2 * ftoutline.points[m].x)) * scale / 3.0f;
259                                                         bezt->vec[2][1] = (ftoutline.points[l].y + (2 * ftoutline.points[m].y)) * scale / 3.0f;
260                                                         bezt->h2 = HD_FREE;
261                                                 }
262                                                 else {
263                                                         bezt->vec[2][0] = ftoutline.points[l].x * scale - (ftoutline.points[l].x - ftoutline.points[m].x) * scale / 3.0f;
264                                                         bezt->vec[2][1] = ftoutline.points[l].y * scale - (ftoutline.points[l].y - ftoutline.points[m].y) * scale / 3.0f;
265                                                         bezt->h2 = HD_VECT;
266                                                 }
267                                         }
268
269                                         /* get the handles that are aligned, tricky...
270                                          * dist_to_line_v2, check if the three beztriple points are on one line
271                                          * len_squared_v2v2, see if there's a distance between the three points
272                                          * len_squared_v2v2 again, to check the angle between the handles
273                                          * finally, check if one of them is a vector handle */
274                                         if ((bezt->h1 != HD_VECT && bezt->h2 != HD_VECT) &&
275                                             (dist_to_line_v2(bezt->vec[0], bezt->vec[1], bezt->vec[2]) < 0.001f) &&
276                                             (len_squared_v2v2(bezt->vec[0], bezt->vec[1]) > 0.0001f * 0.0001f) &&
277                                             (len_squared_v2v2(bezt->vec[1], bezt->vec[2]) > 0.0001f * 0.0001f) &&
278                                             (len_squared_v2v2(bezt->vec[0], bezt->vec[2]) > 0.0002f * 0.0001f) &&
279                                             (len_squared_v2v2(bezt->vec[0], bezt->vec[2]) >
280                                              maxf(len_squared_v2v2(bezt->vec[0], bezt->vec[1]),
281                                                   len_squared_v2v2(bezt->vec[1], bezt->vec[2]))))
282                                         {
283                                                 bezt->h1 = bezt->h2 = HD_ALIGN;
284                                         }
285                                         bezt->radius = 1.0f;
286                                         bezt++;
287                                 }
288                         }
289                 }
290                 if (npoints) MEM_freeN(npoints);
291                 if (onpoints) MEM_freeN(onpoints);      
292         }
293 }
294
295 static int objchr_to_ftvfontdata(VFont *vfont, FT_ULong charcode)
296 {
297         // Freetype2
298         FT_Face face;
299         struct TmpFont *tf;
300         
301         // Find the correct FreeType font
302         tf = BKE_vfont_find_tmpfont(vfont);
303         
304         // What, no font found. Something strange here
305         if (!tf) return FALSE;
306         
307         // Load the font to memory
308         if (tf->pf) {
309                 err = FT_New_Memory_Face(library,
310                                          tf->pf->data,
311                                          tf->pf->size,
312                                          0,
313                                          &face);
314                 if (err) return FALSE;
315         }
316         else {
317                 err = TRUE;
318                 return FALSE;
319         }
320                 
321         // Read the char
322         freetypechar_to_vchar(face, charcode, vfont->data);
323         
324         // And everything went ok
325         return TRUE;
326 }
327
328
329 static VFontData *objfnt_to_ftvfontdata(PackedFile *pf)
330 {
331         // Variables
332         FT_Face face;
333         FT_ULong charcode = 0, lcode;
334         FT_UInt glyph_index;
335         const char *fontname;
336         VFontData *vfd;
337
338 #if 0
339         FT_CharMap found = 0;
340         FT_CharMap charmap;
341         FT_UShort my_platform_id = TT_PLATFORM_MICROSOFT;
342         FT_UShort my_encoding_id = TT_MS_ID_UNICODE_CS;
343         int n;
344 #endif
345
346         // load the freetype font
347         err = FT_New_Memory_Face(library,
348                                  pf->data,
349                                  pf->size,
350                                  0,
351                                  &face);
352
353         if (err) return NULL;
354
355 #if 0
356         for (n = 0; n < face->num_charmaps; n++)
357         {
358                 charmap = face->charmaps[n];
359                 if (charmap->platform_id == my_platform_id &&
360                     charmap->encoding_id == my_encoding_id)
361                 {
362                         found = charmap;
363                         break;
364                 }
365         }
366
367         if (!found) { return NULL; }
368
369         // now, select the charmap for the face object
370         err = FT_Set_Charmap(face, found);
371         if (err) { return NULL; }
372 #endif
373
374         // allocate blender font
375         vfd = MEM_callocN(sizeof(*vfd), "FTVFontData");
376
377         // get the name
378         fontname = FT_Get_Postscript_Name(face);
379         BLI_strncpy(vfd->name, (fontname == NULL) ? "" : fontname, sizeof(vfd->name));
380
381         // Extract the first 256 character from TTF
382         lcode = charcode = FT_Get_First_Char(face, &glyph_index);
383
384         // No charmap found from the ttf so we need to figure it out
385         if (glyph_index == 0) {
386                 FT_CharMap found = NULL;
387                 FT_CharMap charmap;
388                 int n;
389
390                 for (n = 0; n < face->num_charmaps; n++) {
391                         charmap = face->charmaps[n];
392                         if (charmap->encoding == FT_ENCODING_APPLE_ROMAN) {
393                                 found = charmap;
394                                 break;
395                         }
396                 }
397
398                 err = FT_Set_Charmap(face, found);
399
400                 if (err)
401                         return NULL;
402
403                 lcode = charcode = FT_Get_First_Char(face, &glyph_index);
404         }
405
406         // Load characters
407         while (charcode < 256) {
408                 // Generate the font data
409                 freetypechar_to_vchar(face, charcode, vfd);
410
411                 // Next glyph
412                 charcode = FT_Get_Next_Char(face, charcode, &glyph_index);
413
414                 // Check that we won't start infinite loop
415                 if (charcode <= lcode)
416                         break;
417                 lcode = charcode;
418         }
419
420         return vfd;     
421 }
422
423
424 static int check_freetypefont(PackedFile *pf)
425 {
426         FT_Face face;
427         FT_GlyphSlot glyph;
428         FT_UInt glyph_index;
429 #if 0
430         FT_CharMap charmap;
431         FT_CharMap found;
432         FT_UShort my_platform_id = TT_PLATFORM_MICROSOFT;
433         FT_UShort my_encoding_id = TT_MS_ID_UNICODE_CS;
434         int n;
435 #endif
436         int success = 0;
437
438         err = FT_New_Memory_Face(library,
439                                  pf->data,
440                                  pf->size,
441                                  0,
442                                  &face);
443         if (err) {
444                 success = 0;
445                 //XXX error("This is not a valid font");
446         }
447         else {
448
449 #if 0
450                 for (n = 0; n < face->num_charmaps; n++) {
451                         charmap = face->charmaps[n];
452                         if (charmap->platform_id == my_platform_id && charmap->encoding_id == my_encoding_id) {
453                                 found = charmap;
454                                 break;
455                         }
456                 }
457
458                 if (!found) { return 0; }
459
460                 // now, select the charmap for the face object 
461                 err = FT_Set_Charmap(face, found);
462                 if (err) { return 0; }
463 #endif
464
465                 glyph_index = FT_Get_Char_Index(face, 'A');
466                 err = FT_Load_Glyph(face, glyph_index, FT_LOAD_NO_SCALE | FT_LOAD_NO_BITMAP);
467                 if (err) success = 0;
468                 else {
469                         glyph = face->glyph;
470                         if (glyph->format == ft_glyph_format_outline) {
471                                 success = 1;
472                         }
473                         else {
474                                 //XXX error("Selected Font has no outline data");
475                                 success = 0;
476                         }
477                 }
478         }
479         
480         return success;
481 }
482
483
484 VFontData *BLI_vfontdata_from_freetypefont(PackedFile *pf)
485 {
486         VFontData *vfd = NULL;
487         int success = 0;
488
489         //init Freetype 
490         err = FT_Init_FreeType(&library);
491         if (err) {
492                 //XXX error("Failed to load the Freetype font library");
493                 return NULL;
494         }
495
496         success = check_freetypefont(pf);
497         
498         if (success) {
499                 vfd = objfnt_to_ftvfontdata(pf);
500         }
501
502         //free Freetype
503         FT_Done_FreeType(library);
504         
505         return vfd;
506 }
507
508 int BLI_vfontchar_from_freetypefont(VFont *vfont, unsigned long character)
509 {
510         int success = FALSE;
511
512         if (!vfont) return FALSE;
513
514         // Init Freetype
515         err = FT_Init_FreeType(&library);
516         if (err) {
517                 //XXX error("Failed to load the Freetype font library");
518                 return 0;
519         }
520
521         // Load the character
522         success = objchr_to_ftvfontdata(vfont, character);
523         if (success == FALSE) return FALSE;
524
525         // Free Freetype
526         FT_Done_FreeType(library);
527
528         // Ahh everything ok
529         return TRUE;
530 }
531
532 #if 0
533
534 // Freetype2 Outline struct
535
536 typedef struct  FT_Outline_
537   {
538         short       n_contours;      /* number of contours in glyph        */
539         short       n_points;        /* number of points in the glyph      */
540
541         FT_Vector*  points;          /* the outline's points               */
542         char*       tags;            /* the points flags                   */
543         short*      contours;        /* the contour end points             */
544
545         int         flags;           /* outline masks                      */
546 } FT_Outline;
547
548 #endif
549
550 /*
551  * from: http://www.freetype.org/freetype2/docs/glyphs/glyphs-6.html#section-1
552  *
553  * Vectorial representation of Freetype glyphs
554  *
555  * The source format of outlines is a collection of closed paths called "contours". Each contour is
556  * made of a series of line segments and bezier arcs. Depending on the file format, these can be
557  * second-order or third-order polynomials. The former are also called quadratic or conic arcs, and
558  * they come from the TrueType format. The latter are called cubic arcs and mostly come from the
559  * Type1 format.
560  *
561  * Each arc is described through a series of start, end and control points. Each point of the outline
562  * has a specific tag which indicates wether it is used to describe a line segment or an arc.
563  *
564  *
565  * The following rules are applied to decompose the contour's points into segments and arcs :
566  *
567  * # two successive "on" points indicate a line segment joining them.
568  *
569  * # one conic "off" point amidst two "on" points indicates a conic bezier arc, the "off" point being
570  *   the control point, and the "on" ones the start and end points.
571  *
572  * # Two successive cubic "off" points amidst two "on" points indicate a cubic bezier arc. There must
573  *   be exactly two cubic control points and two on points for each cubic arc (using a single cubic
574  *   "off" point between two "on" points is forbidden, for example).
575  *
576  * # finally, two successive conic "off" points forces the rasterizer to create (during the scan-line
577  *   conversion process exclusively) a virtual "on" point amidst them, at their exact middle. This
578  *   greatly facilitates the definition of successive conic bezier arcs. Moreover, it's the way
579  *   outlines are described in the TrueType specification.
580  *
581  * Note that it is possible to mix conic and cubic arcs in a single contour, even though no current
582  * font driver produces such outlines.
583  *
584  *                                   *            # on
585  *                                                * off
586  *                                __---__
587  *   #-__                      _--       -_
588  *       --__                _-            -
589  *           --__           #               \
590  *               --__                        #
591  *                   -#
592  *                            Two "on" points
593  *    Two "on" points       and one "conic" point
594  *                             between them
595  *
596  *
597  *
598  *                 *
599  *   #            __      Two "on" points with two "conic"
600  *    \          -  -     points between them. The point
601  *     \        /    \    marked '0' is the middle of the
602  *      -      0      \   "off" points, and is a 'virtual'
603  *       -_  _-       #   "on" point where the curve passes.
604  *         --             It does not appear in the point
605  *                        list.
606  *         *
607  *
608  *
609  *
610  *
611  *         *                # on
612  *                    *     * off
613  *          __---__
614  *       _--       -_
615  *     _-            -
616  *    #               \
617  *                     #
618  *
619  *      Two "on" points
620  *    and two "cubic" point
621  *       between them
622  *
623  *
624  * Each glyph's original outline points are located on a grid of indivisible units. The points are stored
625  * in the font file as 16-bit integer grid coordinates, with the grid origin's being at (0, 0); they thus
626  * range from -16384 to 16383.
627  *
628  * Convert conic to bezier arcs:
629  * Conic P0 P1 P2
630  * Bezier B0 B1 B2 B3
631  * B0=P0
632  * B1=(P0+2*P1)/3
633  * B2=(P2+2*P1)/3
634  * B3=P2
635  *
636  */