OSDN Git Service

0686456181a1bf7a6ea910b0fea0793c9f74e9b0
[kimikage-nscr/kimikage-nscr.git] / nsdfont / src / Glyph.cpp
1 /*\r
2  *      Kimikage NScripter Plugins Project\r
3  *\r
4  *      This software is distributed under a BSD-style license.\r
5  *      See license.txt for more information.\r
6  */\r
7 \r
8 #include "Glyph.h"\r
9 #include <cmath>\r
10 \r
11 namespace nsdfont\r
12 {\r
13 #undef GetGlyphOutline\r
14 \r
15         Glyph::Glyph( void )\r
16         {\r
17                 bufferSize = 0;\r
18                 buffer = NULL;\r
19         }\r
20 \r
21         Glyph::Glyph( const Glyph& glyph )\r
22         {\r
23                 character = glyph.character;\r
24                 memcpy( &metrics, &glyph.metrics, sizeof( metrics ) );\r
25                 bufferSize = glyph.bufferSize;\r
26                 if ( bufferSize > 0 )\r
27                 {\r
28                         buffer = new unsigned char[bufferSize];\r
29                         memcpy( buffer, glyph.buffer, bufferSize );\r
30                 }\r
31                 else\r
32                 {\r
33                         buffer = NULL;\r
34                 }\r
35         }\r
36 \r
37         Glyph::Glyph( unsigned int character )\r
38         {\r
39                 bufferSize = 0;\r
40                 buffer = NULL;\r
41                 this->character = character;\r
42                 const unsigned int index = \r
43                         ( mode & GGO_GLYPH_INDEX ) ? cm.getGlyphIndex( character ) : character;\r
44 \r
45                 // \95K\97v\83o\83b\83t\83@\83T\83C\83Y\81C\83\81\83g\83\8a\83N\83X\8eæ\93¾(lpvBuffer=NULL)\r
46                 const DWORD bufferRequest = GetGlyphOutline( cm.getDC(), index, mode, &metrics, 0, NULL, &transform );\r
47                 if ( bufferRequest == GDI_ERROR ) return;\r
48 \r
49                 if ( bufferRequest == 0 )\r
50                 {\r
51                         // GetGlyphOutline\82Ì\83o\83O\82É\82æ\82è0\82É\82È\82ç\82È\82¢\8fê\8d\87\82ª\82 \82é\r
52                         metrics.gmBlackBoxX = 0;\r
53                         metrics.gmBlackBoxY = 0;\r
54                         return;\r
55                 }\r
56                 // GetGlyphOutline\82Ì\95Ô\82·\83o\83b\83t\83@\83T\83C\83Y\82Í\90M\97p\82µ\82Ä\82Í\82¢\82¯\82È\82¢\82ç\82µ\82¢\r
57                 // \82ä\82¦\82É\83\81\83g\83\8a\83N\83X\82©\82ç\8cv\8eZ\82µ\82½\92l\82Æ\94ä\8ar\82µ\82Ä\91å\82«\82¢\82Ù\82¤\82ð\8dÌ\97p\r
58                 bufferSize = static_cast<size_t>( getHeight() * getPitch() );\r
59                 if ( bufferSize < bufferRequest ) bufferSize = bufferRequest;\r
60 \r
61                 buffer = new unsigned char[bufferSize];\r
62                 GetGlyphOutline( cm.getDC(), index, mode, &metrics, bufferSize, buffer, &transform );\r
63 \r
64                 switch( mode & ~GGO_GLYPH_INDEX )\r
65                 {\r
66                 case GGO_GRAY4_BITMAP:\r
67                         for( size_t i = 0; i < bufferSize; ++i )\r
68                         {\r
69                                 const int alpha = (255 * buffer[i]) >> 4; // 0~16 -> 0~255\r
70                                 buffer[i] = static_cast<unsigned char>( alpha );\r
71                         }\r
72                         break;\r
73                 case GGO_GRAY8_BITMAP: \r
74                         for( size_t i = 0; i < bufferSize; ++i )\r
75                         {\r
76                                 const int alpha = (255 * buffer[i]) >> 6; // 0~64 -> 0~255\r
77                                 buffer[i] = static_cast<unsigned char>( alpha );\r
78                         }\r
79                         break;\r
80                 default:\r
81                         break;\r
82                 }\r
83         }\r
84 \r
85         Glyph::~Glyph(void)\r
86         {\r
87                 if ( NULL != buffer )\r
88                 {\r
89                         delete [] buffer;\r
90                 }\r
91         }\r
92 \r
93         void Glyph::setFont( const LOGFONTA *font )\r
94         {\r
95                 cm.selectFont( font );\r
96                 GetTextMetrics( cm.getDC(), &textMetrics );\r
97                 cache.flush();\r
98         }\r
99 \r
100         void Glyph::setTransformation( double xScale, double xAngle, double yAngle )\r
101         {\r
102                 const double deg2rad = 0.0174532925199432957692369; // pi / 180\r
103                 xAngle *= deg2rad;\r
104                 yAngle *= deg2rad;\r
105                 // \82±\82Ì\95Ï\8a·\8ds\97ñ\82Í\89¡\83x\83N\83g\83\8b\82É\91Î\82·\82é\82à\82Ì\82ç\82µ\82¢\r
106                 const double m11 = xScale * std::cos( yAngle );\r
107                 const double m21 = xScale * std::sin( xAngle );\r
108                 const double m12 = -std::sin( yAngle );\r
109                 const double m22 = std::cos( xAngle );\r
110                 doubleToFixed( m11, transform.eM11 );\r
111                 doubleToFixed( m12, transform.eM12 );\r
112                 doubleToFixed( m21, transform.eM21 );\r
113                 doubleToFixed( m22, transform.eM22 );\r
114         }\r
115 \r
116         void Glyph::doubleToFixed( double d, FIXED &f )\r
117         {\r
118                 int i = static_cast<int>( 0x10000 * d ); \r
119                 f.fract = static_cast<WORD>( i & 0xFFFF );\r
120                 f.value = static_cast<WORD>( i >> 16 );\r
121         }\r
122 \r
123         UINT Glyph::detectMode( void )\r
124         {\r
125                 HMODULE hGdi = GetModuleHandleA( "gdi32.dll" );\r
126                 GetGlyphOutline = reinterpret_cast<GetGlyphOutlinePtr>( GetProcAddress( hGdi, "GetGlyphOutlineW" ) );\r
127                 // GetGlyphOutline = NULL;\r
128                 if ( GetGlyphOutline )\r
129                 {\r
130                         return GGO_GRAY8_BITMAP;\r
131                 }\r
132                 GetGlyphOutline = reinterpret_cast<GetGlyphOutlinePtr>( GetGlyphOutlineA );\r
133                 return GGO_GLYPH_INDEX | GGO_GRAY8_BITMAP;\r
134         }\r
135 \r
136 \r
137         const Glyph *Glyph::get( unsigned int character )\r
138         {\r
139                 return cache.fetch( character );\r
140         }\r
141 \r
142         // ------------------------------------------------------------------------\r
143 \r
144         Glyph::Cache::Cache( size_t capacity )\r
145         {\r
146                 this->capacity = capacity;\r
147                 allocated = 0;\r
148         }\r
149 \r
150         Glyph::Cache::~Cache( void )\r
151         {\r
152                 flush();\r
153         }\r
154 \r
155         const Glyph *Glyph::Cache::fetch( unsigned int character )\r
156         {\r
157                 if ( glyphs.count( character ) <= 0 )\r
158                 {\r
159                         add( character ); // \91\8dÝ\82µ\82È\82¯\82ê\82Î\92Ç\89Á\r
160                 }\r
161                 else\r
162                 {\r
163                         // \90æ\93ª\82É\88Ú\93®\r
164                         order.remove( character ); // \88ê\92U\8dí\8f\9c\r
165                         order.push_front( character ); // \90æ\93ª\82É\8dÄ\91}\93ü\r
166                 }\r
167                 return glyphs[character];\r
168         }\r
169 \r
170         void Glyph::Cache::add( unsigned int character )\r
171         {\r
172                 remove( character ); // \94O\82Ì\82½\82ß\8aù\91\83f\81[\83^\82ð\8dí\8f\9c\r
173                 reduce(); // \92Ç\89Á\82·\82é\83O\83\8a\83t\82ð\8fÁ\82³\82È\82¢\82½\82ß\82É\90æ\82É\8eÀ\8ds\r
174                 const Glyph *g = new Glyph( character );\r
175                 glyphs[character] = g;\r
176                 allocated += g->getBufferSize();\r
177                 order.push_front( character );\r
178         }\r
179 \r
180         void Glyph::Cache::remove( unsigned int character )\r
181         {\r
182                 if ( glyphs.count( character ) > 0 ) // character\82ª\91\8dÝ\82·\82é\82©\r
183                 {\r
184                         const Glyph *g = glyphs[character];\r
185                         allocated -= g->getBufferSize();\r
186                         delete g;\r
187                         glyphs.erase( character );\r
188                         order.remove( character );\r
189                 }\r
190         }\r
191 \r
192         void Glyph::Cache::reduce()\r
193         {\r
194                 while( allocated > capacity )\r
195                 {\r
196                         const unsigned int &character = order.back(); // \8dÅ\8cã\82Ìfetch\82©\82ç\88ê\94Ô\8e\9e\8aÔ\82ª\82½\82Á\82½\95\8e\9a\r
197                         const Glyph *g = glyphs[character];\r
198                         allocated -= g->getBufferSize();\r
199                         delete g;\r
200                         glyphs.erase( character );\r
201                         order.pop_back();\r
202                 }\r
203         }\r
204 \r
205         void Glyph::Cache::flush( void )\r
206         {\r
207                 std::map<unsigned int, const Glyph *>::iterator it;\r
208                 for( it = glyphs.begin(); it != glyphs.end(); ++it )\r
209                 {\r
210                         delete (*it).second;\r
211                 }\r
212                 glyphs.clear();\r
213                 order.clear();\r
214         }\r
215 \r
216 \r
217 \r
218         Glyph::GetGlyphOutlinePtr Glyph::GetGlyphOutline = NULL;\r
219         const UINT Glyph::mode = Glyph::detectMode();\r
220         CharacterMap Glyph::cm;\r
221         TEXTMETRICA Glyph::textMetrics;\r
222         Glyph::Cache Glyph::cache( 1024 * 256 );\r
223         MAT2 Glyph::transform = {{1,0}, {0,0}, {0,0}, {0,1}};\r
224 }\r
225 \r