1 /* -*- Mode: C; indent-tabs-mode: t; tab-width: 4 -*-
2 // ---------------------------------------------------------------------------
4 // Copyright (C) Stephanie Gawroriski <xer@multiphasicapps.net>
5 // ---------------------------------------------------------------------------
6 // SquirrelJME is under the Mozilla Public License Version 2.0.
7 // See license.mkd for licensing and copyright information.
8 // -------------------------------------------------------------------------*/
13 #include "lib/scritchui/scritchui.h"
14 #include "lib/scritchui/scritchuiPencilFont.h"
15 #include "lib/scritchui/scritchuiPencilFontPseudo.h"
16 #include "lib/scritchui/scritchuiTypes.h"
17 #include "sjme/debug.h"
19 static sjme_errorCode
sjme_scritchui_validateChar(
20 sjme_attrInNotNull sjme_scritchui_pencilFont inFont
,
21 sjme_attrInOutNotNull sjme_jint
* inOutCodepoint
)
24 sjme_jboolean isValid
;
26 if (inFont
== NULL
|| inOutCodepoint
== NULL
)
27 return SJME_ERROR_NULL_ARGUMENTS
;
29 /* Determine if this character is even valid. */
30 isValid
= SJME_JNI_FALSE
;
31 if (sjme_error_is(error
= inFont
->api
->metricCharValid(inFont
,
32 *inOutCodepoint
, &isValid
)))
33 return sjme_error_default(error
);
35 /* If it is not valid, then replace with the invalid character. */
41 /* Check to see if zero is valid. */
42 isValid
= SJME_JNI_FALSE
;
43 if (sjme_error_is(error
= inFont
->api
->metricCharValid(inFont
,
44 *inOutCodepoint
, &isValid
)))
45 return sjme_error_default(error
);
47 /* If it is not, then likely the Unicode bad character is used. */
49 *inOutCodepoint
= 0xFFFD;
53 return SJME_ERROR_NONE
;
56 static sjme_jboolean
sjme_scritchui_fontEquals(
57 sjme_attrInNullable sjme_scritchui_pencilFont a
,
58 sjme_attrInNullable sjme_scritchui_pencilFont b
)
63 return SJME_JNI_FALSE
;
66 return SJME_JNI_FALSE
;
69 static sjme_errorCode
sjme_scritchui_fontMetricCharDirection(
70 sjme_attrInNotNull sjme_scritchui_pencilFont inFont
,
71 sjme_attrInPositive sjme_jint inCodepoint
,
72 sjme_attrOutNotNull
sjme_attrInRange(-1, 1) sjme_jint
* outDirection
)
74 if (inFont
== NULL
|| outDirection
== NULL
)
75 return SJME_ERROR_NULL_ARGUMENTS
;
78 return sjme_error_notImplemented(0);
81 static sjme_errorCode
sjme_scritchui_fontMetricCharValid(
82 sjme_attrInNotNull sjme_scritchui_pencilFont inFont
,
83 sjme_attrInPositive sjme_jint inCodepoint
,
84 sjme_attrOutNotNull sjme_jboolean
* outValid
)
86 if (inFont
== NULL
|| outValid
== NULL
)
87 return SJME_ERROR_NULL_ARGUMENTS
;
89 /* Not implemented? */
90 if (inFont
->impl
->metricCharValid
== NULL
)
91 return sjme_error_notImplemented(0);
93 /* Negative codepoints are always invalid. */
96 *outValid
= SJME_JNI_FALSE
;
97 return SJME_ERROR_NONE
;
101 return inFont
->impl
->metricCharValid(inFont
, inCodepoint
, outValid
);
104 static sjme_errorCode
sjme_scritchui_fontMetricFontFace(
105 sjme_attrInNotNull sjme_scritchui_pencilFont inFont
,
106 sjme_attrOutNotNull sjme_scritchui_pencilFontFace
* outFace
)
108 sjme_errorCode error
;
109 sjme_scritchui_pencilFontFace result
;
111 if (inFont
== NULL
|| outFace
== NULL
)
112 return SJME_ERROR_NULL_ARGUMENTS
;
115 if (inFont
->cache
.face
!= 0)
117 *outFace
= inFont
->cache
.face
;
118 return SJME_ERROR_NONE
;
121 /* Not implemented? */
122 if (inFont
->impl
->metricFontFace
== NULL
)
123 return sjme_error_notImplemented(0);
125 /* Load into cache. */
127 if (sjme_error_is(error
= inFont
->impl
->metricFontFace(inFont
,
128 &result
)) || result
== 0)
129 return sjme_error_default(error
);
131 /* Cache and use it. */
132 inFont
->cache
.face
= result
;
134 return SJME_ERROR_NONE
;
137 static sjme_errorCode
sjme_scritchui_fontMetricFontName(
138 sjme_attrInNotNull sjme_scritchui_pencilFont inFont
,
139 sjme_attrInOutNotNull sjme_lpcstr
* outName
)
141 sjme_errorCode error
;
143 if (inFont
== NULL
|| outName
== NULL
)
144 return SJME_ERROR_NULL_ARGUMENTS
;
146 /* Need to load cache? */
147 if (inFont
->cache
.name
== NULL
)
149 /* Not implemented? */
150 if (inFont
->impl
->metricFontName
== NULL
)
151 return sjme_error_notImplemented(0);
153 /* Use internal lookup. */
154 if (sjme_error_is(error
= inFont
->impl
->metricFontName(
155 inFont
, &inFont
->cache
.name
)))
156 return sjme_error_default(error
);
159 /* Return the name. */
160 *outName
= inFont
->cache
.name
;
161 return SJME_ERROR_NONE
;
164 static sjme_errorCode
sjme_scritchui_fontMetricFontStyle(
165 sjme_attrInNotNull sjme_scritchui_pencilFont inFont
,
166 sjme_attrOutNotNull sjme_scritchui_pencilFontStyle
* outStyle
)
168 sjme_errorCode error
;
169 sjme_scritchui_pencilFontStyle result
;
171 if (inFont
== NULL
|| outStyle
== NULL
)
172 return SJME_ERROR_NULL_ARGUMENTS
;
175 if (inFont
->cache
.style
!= 0)
177 *outStyle
= inFont
->cache
.style
;
178 return SJME_ERROR_NONE
;
181 /* Not implemented? */
182 if (inFont
->impl
->metricFontStyle
!= NULL
)
184 /* Load into cache. */
186 if (sjme_error_is(error
= inFont
->impl
->metricFontStyle(inFont
,
187 &result
)) || result
== -1)
188 return sjme_error_default(error
);
191 /* Font has no style otherwise. */
195 /* Cache and use it. */
196 inFont
->cache
.style
= result
;
198 return SJME_ERROR_NONE
;
201 static sjme_errorCode
sjme_scritchui_fontMetricPixelAscent(
202 sjme_attrInNotNull sjme_scritchui_pencilFont inFont
,
203 sjme_attrInValue sjme_jboolean isMax
,
204 sjme_attrOutNotNull sjme_jint
* outAscent
)
206 sjme_errorCode error
;
209 if (inFont
== NULL
|| outAscent
== NULL
)
210 return SJME_ERROR_NULL_ARGUMENTS
;
213 if (inFont
->cache
.ascent
[!!isMax
] != 0)
215 *outAscent
= inFont
->cache
.ascent
[!!isMax
];
216 return SJME_ERROR_NONE
;
219 /* Not implemented? */
220 if (inFont
->impl
->metricPixelAscent
== NULL
)
221 return sjme_error_notImplemented(0);
223 /* Load into cache. */
225 if (sjme_error_is(error
= inFont
->impl
->metricPixelAscent(inFont
,
227 return sjme_error_default(error
);
229 /* Cache and use it. */
230 inFont
->cache
.ascent
[!!isMax
] = result
;
232 return SJME_ERROR_NONE
;
235 static sjme_errorCode
sjme_scritchui_fontMetricPixelBaseline(
236 sjme_attrInNotNull sjme_scritchui_pencilFont inFont
,
237 sjme_attrOutNotNull sjme_jint
* outBaseline
)
239 sjme_errorCode error
;
243 return SJME_ERROR_NULL_ARGUMENTS
;
246 if (inFont
->cache
.baseline
!= 0)
248 *outBaseline
= inFont
->cache
.baseline
;
249 return SJME_ERROR_NONE
;
252 /* Not implemented? */
253 if (inFont
->impl
->metricPixelBaseline
== NULL
)
254 return sjme_error_notImplemented(0);
256 /* Load into cache. */
258 if (sjme_error_is(error
= inFont
->impl
->metricPixelBaseline(inFont
,
260 return sjme_error_default(error
);
262 /* Cache and use it. */
263 inFont
->cache
.baseline
= result
;
264 *outBaseline
= result
;
265 return SJME_ERROR_NONE
;
268 static sjme_errorCode
sjme_scritchui_fontMetricPixelDescent(
269 sjme_attrInNotNull sjme_scritchui_pencilFont inFont
,
270 sjme_attrInValue sjme_jboolean isMax
,
271 sjme_attrOutNotNull sjme_jint
* outDescent
)
273 sjme_errorCode error
;
276 if (inFont
== NULL
|| outDescent
== NULL
)
277 return SJME_ERROR_NULL_ARGUMENTS
;
280 if (inFont
->cache
.descent
[!!isMax
] != 0)
282 *outDescent
= inFont
->cache
.descent
[!!isMax
];
283 return SJME_ERROR_NONE
;
286 /* Not implemented? */
287 if (inFont
->impl
->metricPixelDescent
== NULL
)
288 return sjme_error_notImplemented(0);
290 /* Load into cache. */
292 if (sjme_error_is(error
= inFont
->impl
->metricPixelDescent(inFont
,
294 return sjme_error_default(error
);
296 /* Cache and use it. */
297 inFont
->cache
.descent
[!!isMax
] = result
;
298 *outDescent
= result
;
299 return SJME_ERROR_NONE
;
302 static sjme_errorCode
sjme_scritchui_fontMetricPixelHeight(
303 sjme_attrInNotNull sjme_scritchui_pencilFont inFont
,
304 sjme_attrOutNotNull sjme_jint
* outHeight
)
306 sjme_errorCode error
;
307 sjme_jint leading
, ascent
, descent
;
309 if (inFont
== NULL
|| outHeight
== NULL
)
310 return SJME_ERROR_NULL_ARGUMENTS
;
313 if (inFont
->cache
.height
!= 0)
315 *outHeight
= inFont
->cache
.height
;
316 return SJME_ERROR_NONE
;
319 /* Get all of these parameters. */
321 if (sjme_error_is(error
= inFont
->api
->metricPixelLeading(inFont
,
323 return sjme_error_default(error
);
326 if (sjme_error_is(error
= inFont
->api
->metricPixelAscent(inFont
,
327 SJME_JNI_FALSE
, &ascent
)))
328 return sjme_error_default(error
);
331 if (sjme_error_is(error
= inFont
->api
->metricPixelDescent(inFont
,
332 SJME_JNI_FALSE
, &descent
)))
333 return sjme_error_default(error
);
336 inFont
->cache
.height
= leading
+ ascent
+ descent
;
339 *outHeight
= inFont
->cache
.height
;
340 return SJME_ERROR_NONE
;
343 static sjme_errorCode
sjme_scritchui_fontMetricPixelLeading(
344 sjme_attrInNotNull sjme_scritchui_pencilFont inFont
,
345 sjme_attrOutNotNull sjme_attrOutPositiveNonZero sjme_jint
* outLeading
)
347 sjme_errorCode error
;
350 if (inFont
== NULL
|| outLeading
== NULL
)
351 return SJME_ERROR_NULL_ARGUMENTS
;
354 if (inFont
->cache
.leading
!= 0)
356 *outLeading
= inFont
->cache
.leading
;
357 return SJME_ERROR_NONE
;
360 /* Not implemented? */
361 if (inFont
->impl
->metricPixelLeading
== NULL
)
362 return sjme_error_notImplemented(0);
364 /* Load into cache. */
366 if (sjme_error_is(error
= inFont
->impl
->metricPixelLeading(inFont
,
368 return sjme_error_default(error
);
370 /* Cache and use it. */
371 inFont
->cache
.leading
= result
;
372 *outLeading
= result
;
373 return SJME_ERROR_NONE
;
376 static sjme_errorCode
sjme_scritchui_fontMetricPixelSize(
377 sjme_attrInNotNull sjme_scritchui_pencilFont inFont
,
378 sjme_attrOutNotNull sjme_attrOutPositiveNonZero sjme_jint
* outSize
)
380 sjme_errorCode error
;
383 if (inFont
== NULL
|| outSize
== NULL
)
384 return SJME_ERROR_NULL_ARGUMENTS
;
387 if (inFont
->cache
.pixelSize
!= 0)
389 *outSize
= inFont
->cache
.pixelSize
;
390 return SJME_ERROR_NONE
;
393 /* Not implemented? */
394 if (inFont
->impl
->metricPixelSize
== NULL
)
395 return sjme_error_notImplemented(0);
397 /* Load into cache. */
399 if (sjme_error_is(error
= inFont
->impl
->metricPixelSize(inFont
,
400 &result
)) || result
<= 0)
401 return sjme_error_default(error
);
403 /* Cache and use it. */
404 inFont
->cache
.pixelSize
= result
;
406 return SJME_ERROR_NONE
;
409 static sjme_errorCode
sjme_scritchui_fontPixelCharWidth(
410 sjme_attrInNotNull sjme_scritchui_pencilFont inFont
,
411 sjme_attrInPositive sjme_jint inCodepoint
,
412 sjme_attrOutNotNull sjme_attrOutPositiveNonZero sjme_jint
* outWidth
)
414 sjme_errorCode error
;
417 if (inFont
== NULL
|| outWidth
== NULL
)
418 return SJME_ERROR_NULL_ARGUMENTS
;
420 /* Not implemented? */
421 if (inFont
->impl
->pixelCharWidth
== NULL
)
422 return sjme_error_notImplemented(0);
424 /* Codepoints with no actual length. */
425 if (inCodepoint
== '\r' || inCodepoint
== '\n' ||
426 inCodepoint
== '\v' || inCodepoint
== '\f' ||
427 inCodepoint
== 0x2060 || inCodepoint
== 0xFEFF ||
428 inCodepoint
== 0x200D || inCodepoint
== 0x200C)
431 return SJME_ERROR_NONE
;
434 /* Treat tabs as spaces. */
435 if (inCodepoint
== '\t')
438 /* Validate character to use. */
439 if (sjme_error_is(error
= sjme_scritchui_validateChar(inFont
,
441 return sjme_error_default(error
);
445 if (sjme_error_is(error
= inFont
->impl
->pixelCharWidth(inFont
,
446 inCodepoint
, &result
)) || result
< 0)
447 return sjme_error_default(error
);
451 return SJME_ERROR_NONE
;
454 static sjme_errorCode
sjme_scritchui_fontRenderBitmap(
455 sjme_attrInNotNull sjme_scritchui_pencilFont inFont
,
456 sjme_attrInPositive sjme_jint inCodepoint
,
457 sjme_attrInNotNull sjme_jubyte
* buf
,
458 sjme_attrInPositive sjme_jint bufOff
,
459 sjme_attrInPositive sjme_jint bufScanLen
,
460 sjme_attrInPositive sjme_jint bufHeight
,
461 sjme_attrOutNullable sjme_jint
* outOffX
,
462 sjme_attrOutNullable sjme_jint
* outOffY
)
464 sjme_errorCode error
;
466 if (inFont
== NULL
|| buf
== NULL
)
467 return SJME_ERROR_NULL_ARGUMENTS
;
470 return SJME_ERROR_INVALID_ARGUMENT
;
472 if (bufOff
< 0 || bufScanLen
<= 0 || bufHeight
<= 0 ||
473 (bufHeight
* bufScanLen
) < 0 ||
474 (bufOff
+ (bufHeight
* bufScanLen
)) < 0)
475 return SJME_ERROR_INDEX_OUT_OF_BOUNDS
;
477 /* Not implemented? */
478 if (inFont
->impl
->renderBitmap
== NULL
)
479 return sjme_error_notImplemented(0);
481 /* Validate glyph. */
482 if (sjme_error_is(error
= sjme_scritchui_validateChar(inFont
,
484 return sjme_error_default(error
);
486 /* Render resultant bitmap. */
487 if (sjme_error_is(error
= inFont
->impl
->renderBitmap(inFont
,
488 inCodepoint
, buf
, bufOff
, bufScanLen
, bufHeight
, outOffX
, outOffY
)))
489 return sjme_error_default(error
);
492 return SJME_ERROR_NONE
;
495 static sjme_errorCode
sjme_scritchui_fontRenderChar(
496 sjme_attrInNotNull sjme_scritchui_pencilFont inFont
,
497 sjme_attrInPositive sjme_jint inCodepoint
,
498 sjme_attrInNotNull sjme_scritchui_pencil inPencil
,
499 sjme_attrInValue sjme_jint xPos
,
500 sjme_attrInNotNull sjme_jint yPos
,
501 sjme_attrOutNullable sjme_jint
* nextXPos
,
502 sjme_attrOutNullable sjme_jint
* nextYPos
)
505 return SJME_ERROR_NULL_ARGUMENTS
;
508 return sjme_error_notImplemented(0);
511 static sjme_errorCode
sjme_scritchui_fontStringWidth(
512 sjme_attrInNotNull sjme_scritchui_pencilFont inFont
,
513 sjme_attrInNotNull
const sjme_charSeq
* s
,
514 sjme_attrInPositive sjme_jint o
,
515 sjme_attrInPositive sjme_jint l
,
516 sjme_attrOutNotNull sjme_jint
* outWidth
)
518 sjme_errorCode error
;
519 sjme_jint seqLen
, at
, cw
, result
, maxResult
;
522 if (inFont
== NULL
|| s
== NULL
|| outWidth
== NULL
)
523 return SJME_ERROR_NULL_ARGUMENTS
;
525 if (o
< 0 || l
< 0 || (o
+ l
) < 0)
526 return SJME_ERROR_INDEX_OUT_OF_BOUNDS
;
528 /* Get sequence length for further checking. */
530 if (sjme_error_is(error
= sjme_charSeq_length(s
,
531 &seqLen
)) || seqLen
< 0)
532 return sjme_error_default(error
);
535 if ((o
+ l
) > seqLen
)
536 return SJME_ERROR_INDEX_OUT_OF_BOUNDS
;
538 /* Defaults to zero length. */
542 /* Process each character within the sequence. */
543 for (at
= 0; at
< l
; at
++)
545 /* Get the next character to check. */
547 if (sjme_error_is(error
= sjme_charSeq_charAt(
549 return sjme_error_default(error
);
552 if (c
== '\r' || c
== '\n')
558 /* Determine character width. */
560 if (sjme_error_is(error
= inFont
->api
->pixelCharWidth(inFont
,
562 return sjme_error_default(error
);
568 if (result
> maxResult
)
573 *outWidth
= maxResult
;
574 return SJME_ERROR_NONE
;
577 /** Functions for basic font support. */
578 static const sjme_scritchui_pencilFontFunctions sjme_scritchui_fontFunctions
=
580 .equals
= sjme_scritchui_fontEquals
,
581 .metricCharDirection
= sjme_scritchui_fontMetricCharDirection
,
582 .metricCharValid
= sjme_scritchui_fontMetricCharValid
,
583 .metricFontFace
= sjme_scritchui_fontMetricFontFace
,
584 .metricFontName
= sjme_scritchui_fontMetricFontName
,
585 .metricFontStyle
= sjme_scritchui_fontMetricFontStyle
,
586 .metricPixelAscent
= sjme_scritchui_fontMetricPixelAscent
,
587 .metricPixelBaseline
= sjme_scritchui_fontMetricPixelBaseline
,
588 .metricPixelDescent
= sjme_scritchui_fontMetricPixelDescent
,
589 .metricPixelHeight
= sjme_scritchui_fontMetricPixelHeight
,
590 .metricPixelLeading
= sjme_scritchui_fontMetricPixelLeading
,
591 .metricPixelSize
= sjme_scritchui_fontMetricPixelSize
,
592 .pixelCharWidth
= sjme_scritchui_fontPixelCharWidth
,
593 .renderBitmap
= sjme_scritchui_fontRenderBitmap
,
594 .renderChar
= sjme_scritchui_fontRenderChar
,
595 .stringWidth
= sjme_scritchui_fontStringWidth
,
598 sjme_errorCode
sjme_scritchui_core_intern_fontBuiltin(
599 sjme_attrInNotNull sjme_scritchui inState
,
600 sjme_attrOutNotNull sjme_scritchui_pencilFont
* outFont
)
602 sjme_scritchui topState
;
604 if (inState
== NULL
|| outFont
== NULL
)
605 return SJME_ERROR_NULL_ARGUMENTS
;
607 /* If we are using a top level state, grab the font from there so */
608 /* that we have a consistent default to use. */
609 topState
= sjme_atomic_sjme_pointer_get(&inState
->topState
);
610 if (topState
!= NULL
)
611 return topState
->api
->fontBuiltin(topState
, outFont
);
613 return inState
->api
->fontBuiltin(inState
, outFont
);
616 sjme_errorCode
sjme_scritchui_core_fontDerive(
617 sjme_attrInNotNull sjme_scritchui inState
,
618 sjme_attrInNotNull sjme_scritchui_pencilFont inFont
,
619 sjme_attrInValue sjme_scritchui_pencilFontStyle inStyle
,
620 sjme_attrInPositiveNonZero sjme_jint inPixelSize
,
621 sjme_attrOutNotNull sjme_scritchui_pencilFont
* outDerived
)
623 sjme_errorCode error
;
624 sjme_scritchui_pencilFontStyle wasStyle
;
625 sjme_jint wasPixelSize
;
627 if (inState
== NULL
|| inFont
== NULL
|| outDerived
== NULL
)
628 return SJME_ERROR_NULL_ARGUMENTS
;
630 if (inPixelSize
<= 0)
631 return SJME_ERROR_INVALID_ARGUMENT
;
634 inStyle
&= SJME_SCRITCHUI_PENCIL_FONT_STYLE_ALL
;
636 /* Get old font properties. */
639 if (sjme_error_is(error
= inFont
->api
->metricFontStyle(inFont
,
641 sjme_error_is(error
= inFont
->api
->metricPixelSize(inFont
,
643 return sjme_error_default(error
);
645 /* If the font is the same, do nothing. */
646 if (wasStyle
== inStyle
&& wasPixelSize
== inPixelSize
)
648 *outDerived
= inFont
;
649 return SJME_ERROR_NONE
;
652 /* Create pseudo font. */
653 return sjme_scritchui_core_fontPseudo(inState
, inFont
, inStyle
,
654 inPixelSize
, outDerived
);
657 sjme_jint
sjme_scritchui_pencilFontScanLen(
658 sjme_attrInPositive sjme_jint w
)
660 return (w
>> 3) + ((w
& 7) != 0 ? 1 : 0);
663 sjme_errorCode
sjme_scritchui_newPencilFontStatic(
664 sjme_scritchui_pencilFont inOutFont
)
666 if (inOutFont
== NULL
)
667 return SJME_ERROR_NULL_ARGUMENTS
;
669 if (inOutFont
->impl
== NULL
)
670 return SJME_ERROR_ILLEGAL_STATE
;
672 /* Set base fields. */
673 inOutFont
->api
= &sjme_scritchui_fontFunctions
;
676 return SJME_ERROR_NONE
;