C++Builderで、文字に任意の角度で斜体をかけて描画する方法。
基本的に文字も点の集まりなので、その点の位置をずらして変形する。
具体的には、
①文字をパスでImage等のCanvasに描く
②パスから点の集合を取得する
③点の位置をずらした新たな点集合を作成
④変形用の点集合でCanvasに描く
という手順になる。
Vclにはパスを扱うクラスはないので、WindowsAPIを直接使うことになる。
以下、サンプル。FormにTrackBarとImageを置いた例。
(boost::shared_arrayを使っているが、生ポインタをnewしてももちろん動く)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 |
#include <windows.h> #include <boost/shared_array.hpp> #include <math.h> using TPointSharedArray = boost::shared_array<TPoint>; const double PI = 3.14159265; void __fastcall TForm1::TrackBar1Change(TObject *Sender) { Image1->Picture = NULL; Image1->Picture->Bitmap->Width = Image1->Width; Image1->Picture->Bitmap->Height = Image1->Height; // const int angle = TrackBar1->Position; const String Str( L"C++Builder" ); const TPoint DrawPt( 100, 50 ); // 文字描画の開始位置 const int FontSize = 50; // // 描いた文字の点の情報を取得する //------------------------------------------------- // 文字をパスで描く TCanvas* Canvas = Image1->Canvas; BeginPath( Canvas->Handle ); Canvas->Brush->Style = bsClear; Canvas->Font->Size = FontSize; Canvas->Pen->Color = clBlack; Canvas->TextOut( DrawPt.X, DrawPt.Y, Str ); EndPath( Canvas->Handle ); //パスを直線の集まりに変換 FlattenPath( Canvas->Handle ); // パス内の点の数を取得 const int count = GetPath( Canvas->Handle, NULL, NULL, 0 ); // 情報を格納する配列の作成: boost::shared_array<TPoint> points( new TPoint[count] ); boost::shared_array<char> kinds( new char[count] ); // パス内の点情報を配列に格納する GetPath( Canvas->Handle, (LPPOINT)(points.get()), (LPBYTE)(kinds.get()), count ); // // 変形した点の情報を取得する //--------------------------------------------------- // 変形した点の配列を取得する(別関数) const int text_height = Canvas->TextHeight( Str ); const int text_width = Canvas->TextWidth( Str ); TPointSharedArray new_points = get_points_transformed_parallelogram_horz ( points, count, text_width, text_height, angle, DrawPt ); // 変形した点の情報で文字を描く //--------------------------------------------------- // 変形したパス情報をCanvasに戻す BeginPath( Canvas->Handle ); PolyDraw( Canvas->Handle, (LPPOINT)(new_points.get()), (LPBYTE)(kinds.get()), count ); EndPath( Canvas->Handle ); // パス情報に従ってCanvasに描く Canvas->Brush->Color = clBlack; StrokeAndFillPath( Canvas->Handle ); } //------------------------------------------------- // 変形した点集合を取得する関数 //------------------------------------------------- TPointSharedArray TForm1::get_points_transformed_parallelogram_horz ( const TPointSharedArray& org_rect_points, const int point_count, const int org_width, const int org_height, const int angle, const TPoint& org_offset )const { TPointSharedArray new_points( new TPoint[point_count] ); const double max_offset = org_height * tan( angle * PI / 180.0 ); for( int i = 0; i < point_count; ++i ) { // Y位置はそのまま new_points[i].Y = org_rect_points[i].Y; // X位置をずらしていく TPoint OrgInnerPt = org_rect_points[i]; OrgInnerPt.Offset( org_offset.X, -org_offset.Y ); const double height_per = OrgInnerPt.Y / (double)org_height; new_points[i].X = org_rect_points[i].X + max_offset * ( 1.0 - height_per ); } return new_points; } |
こんな感じで変形する。
点の情報をずらすだけなので、さまざまな形に変形可能。
たとえば文字列に楕円が食い込んだように変形させる関数のサンプル。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
TPointSharedArray TForm1::get_points_transformed_bittenbycircle_horz ( const TPointSharedArray& org_rect_points, const int point_count, const int org_width, const int org_height, const double bitten_per, const TPoint& org_offset )const { if( bitten_per <= 0.0 ) return org_rect_points; const double bitten = org_height * bitten_per;// 0.0 <= bitten_per <= 1.0 const double radius = ( pow( bitten, 2 ) + pow( org_width / 2.0 , 2 ) ) / ( bitten * 2.0 ); const double distance = radius - bitten; TPointSharedArray org_points_nooffset( new TPoint[point_count] ); for( int i = 0; i < point_count; ++i ) { TPoint pt = org_rect_points[i]; pt.Offset( -org_offset.X, -org_offset.Y ); org_points_nooffset[i] = pt; } TPointSharedArray new_points( new TPoint[point_count] ); // Calc At Not org_offset: for( int i = 0; i < point_count; ++i ) { new_points[i].X = org_points_nooffset[i].x; const double triangle_bottom_line_width = fabs( org_width / 2.0 - org_points_nooffset[i].x ); const double triangle_height = sqrt( pow( (double)radius, 2 ) - pow( (double)triangle_bottom_line_width, 2 ) ); const double bitten_from_rect_top = triangle_height - distance; const double height_per_of_point_in_rect = ( org_height - org_points_nooffset[i].y ) / (double)org_height; const double new_height_from_bottom = height_per_of_point_in_rect * ( org_height - bitten_from_rect_top ); new_points[i].Y = org_height - new_height_from_bottom; } // Offset New Points: for( int i = 0; i < point_count; ++i ) { new_points[i].Offset( org_offset.X, org_offset.Y ); } // return new_points; } |
こんな感じで変形する。
C++Builder Berlin Update2 / Windows7(Sp1)で確認。