@@ -33,6 +33,7 @@ const ishihara_input = {
3333 load_image ( ) {
3434 document . getElementById ( 'image_upload' ) . click ( ) ;
3535 } ,
36+ text : '' ,
3637 circular : true ,
3738 resize : true ,
3839 edge_detection : true ,
@@ -67,6 +68,7 @@ const ishihara_input = {
6768
6869 generating = true ;
6970
71+ if ( ishihara_input . text ) renderText ( ) ;
7072 const img_data = img_ctx . getImageData ( 0 , 0 , canvas . width , canvas . height ) ;
7173 ctx . fillStyle = ishihara_input . background_color ;
7274 ctx . fillRect ( 0 , 0 , canvas . width , canvas . height ) ;
@@ -133,6 +135,67 @@ const ishihara_input = {
133135 }
134136} ;
135137
138+ function renderText ( ) {
139+ img_ctx . fillStyle = 'white' ;
140+ img_ctx . fillRect ( 0 , 0 , img_canvas . width , img_canvas . height ) ;
141+ ctx . fillStyle = 'white' ;
142+ ctx . fillRect ( 0 , 0 , canvas . width , canvas . height ) ;
143+
144+ const text = ishihara_input . text ;
145+ if ( ! text ) return ;
146+
147+ const cw = img_canvas . width ;
148+ const ch = img_canvas . height ;
149+ const maxLineWidth = ishihara_input . circular
150+ ? Math . min ( cw , ch ) * 0.75
151+ : cw * 0.95 ;
152+
153+ const wrapText = ( fontSize ) => {
154+ img_ctx . font = `bold ${ fontSize } px sans-serif` ;
155+ const lines = [ ] ;
156+ for ( const paragraph of text . split ( '\n' ) ) {
157+ let currentLine = '' ;
158+ for ( const word of paragraph . split ( ' ' ) ) {
159+ const testLine = currentLine ? `${ currentLine } ${ word } ` : word ;
160+ if ( currentLine && img_ctx . measureText ( testLine ) . width > maxLineWidth ) {
161+ lines . push ( currentLine ) ;
162+ currentLine = word ;
163+ } else {
164+ currentLine = testLine ;
165+ }
166+ }
167+ lines . push ( currentLine ) ;
168+ }
169+ return lines ;
170+ } ;
171+
172+ let lo = 1 , hi = 4000 ;
173+ while ( hi - lo > 1 ) {
174+ const mid = ( lo + hi ) >> 1 ;
175+ const lines = wrapText ( mid ) ;
176+ const totalHeight = lines . length * mid * 1.2 ;
177+ const maxW = Math . max ( ...lines . map ( l => img_ctx . measureText ( l ) . width ) ) ;
178+ const fits = ishihara_input . circular
179+ ? Math . hypot ( maxW , totalHeight ) <= Math . min ( cw , ch ) * 0.9
180+ : maxW <= cw * 0.95 && totalHeight <= ch * 0.9 ;
181+ if ( fits ) lo = mid ; else hi = mid ;
182+ }
183+
184+ const lines = wrapText ( lo ) ;
185+ const lineHeight = lo * 1.2 ;
186+ const firstLineY = ch / 2 - ( lines . length - 1 ) * lineHeight / 2 ;
187+
188+ img_ctx . font = `bold ${ lo } px sans-serif` ;
189+ img_ctx . fillStyle = 'black' ;
190+ img_ctx . textAlign = 'center' ;
191+ img_ctx . textBaseline = 'middle' ;
192+ for ( let i = 0 ; i < lines . length ; i ++ ) {
193+ img_ctx . fillText ( lines [ i ] , cw / 2 , firstLineY + i * lineHeight ) ;
194+ }
195+
196+ ctx . drawImage ( img_canvas , 0 , 0 , canvas . width , canvas . height ) ;
197+ }
198+
136199function set_colors_folders ( ) {
137200 for ( let i = 0 ; i < 6 ; i ++ ) {
138201 hide_gui_element ( colors_on_folder , 'color_on' + i , i >= ishihara_input . n_colors_on ) ;
@@ -226,6 +289,7 @@ const gui = new dat.GUI({
226289gui . remember ( ishihara_input ) ;
227290
228291gui . add ( ishihara_input , 'load_image' ) . name ( 'Load image' ) ;
292+ gui . add ( ishihara_input , 'text' ) . name ( 'Text' ) . onChange ( ( ) => renderText ( ) ) ;
229293gui . add ( ishihara_input , 'circular' ) . name ( 'Circular' ) ;
230294gui . add ( ishihara_input , 'resize' ) . name ( 'Resize' ) ;
231295gui . add ( ishihara_input , 'edge_detection' ) . name ( 'Edge detection' ) ;
0 commit comments