Always have fallback element colors; Remove annotations from native method as there...
[SquirrelJME.git] / nanocoat / lib / scritchui / scritchPencilFont.c
blob31a34b475fd5fa9282e3241d0c4fa06c57c43baf
1 /* -*- Mode: C; indent-tabs-mode: t; tab-width: 4 -*-
2 // ---------------------------------------------------------------------------
3 // SquirrelJME
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 // -------------------------------------------------------------------------*/
10 #include <stdio.h>
11 #include <stdlib.h>
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)
23 sjme_errorCode error;
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. */
36 if (!isValid)
38 /* Try zero first. */
39 *inOutCodepoint = 0;
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. */
48 if (!isValid)
49 *inOutCodepoint = 0xFFFD;
52 /* Success! */
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)
60 if (a == NULL)
61 return b == NULL;
62 else if (b == NULL)
63 return SJME_JNI_FALSE;
65 sjme_todo("Impl?");
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;
77 sjme_todo("Impl?");
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. */
94 if (inCodepoint < 0)
96 *outValid = SJME_JNI_FALSE;
97 return SJME_ERROR_NONE;
100 /* Forward. */
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;
114 /* Cached? */
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. */
126 result = 0;
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;
133 *outFace = 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;
174 /* Cached? */
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. */
185 result = -1;
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. */
192 else
193 result = 0;
195 /* Cache and use it. */
196 inFont->cache.style = result;
197 *outStyle = 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;
207 sjme_jint result;
209 if (inFont == NULL || outAscent == NULL)
210 return SJME_ERROR_NULL_ARGUMENTS;
212 /* Cached? */
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. */
224 result = 0;
225 if (sjme_error_is(error = inFont->impl->metricPixelAscent(inFont,
226 isMax, &result)))
227 return sjme_error_default(error);
229 /* Cache and use it. */
230 inFont->cache.ascent[!!isMax] = result;
231 *outAscent = 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;
240 sjme_jint result;
242 if (inFont == NULL)
243 return SJME_ERROR_NULL_ARGUMENTS;
245 /* Cached? */
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. */
257 result = 0;
258 if (sjme_error_is(error = inFont->impl->metricPixelBaseline(inFont,
259 &result)))
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;
274 sjme_jint result;
276 if (inFont == NULL || outDescent == NULL)
277 return SJME_ERROR_NULL_ARGUMENTS;
279 /* Cached? */
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. */
291 result = 0;
292 if (sjme_error_is(error = inFont->impl->metricPixelDescent(inFont,
293 isMax, &result)))
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;
312 /* Cached? */
313 if (inFont->cache.height != 0)
315 *outHeight = inFont->cache.height;
316 return SJME_ERROR_NONE;
319 /* Get all of these parameters. */
320 leading = 0;
321 if (sjme_error_is(error = inFont->api->metricPixelLeading(inFont,
322 &leading)))
323 return sjme_error_default(error);
325 ascent = 0;
326 if (sjme_error_is(error = inFont->api->metricPixelAscent(inFont,
327 SJME_JNI_FALSE, &ascent)))
328 return sjme_error_default(error);
330 descent = 0;
331 if (sjme_error_is(error = inFont->api->metricPixelDescent(inFont,
332 SJME_JNI_FALSE, &descent)))
333 return sjme_error_default(error);
335 /* Calculate. */
336 inFont->cache.height = leading + ascent + descent;
338 /* Success! */
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;
348 sjme_jint result;
350 if (inFont == NULL || outLeading == NULL)
351 return SJME_ERROR_NULL_ARGUMENTS;
353 /* Cached? */
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. */
365 result = 0;
366 if (sjme_error_is(error = inFont->impl->metricPixelLeading(inFont,
367 &result)))
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;
381 sjme_jint result;
383 if (inFont == NULL || outSize == NULL)
384 return SJME_ERROR_NULL_ARGUMENTS;
386 /* Cached? */
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. */
398 result = -1;
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;
405 *outSize = 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;
415 sjme_jint result;
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)
430 *outWidth = 0;
431 return SJME_ERROR_NONE;
434 /* Treat tabs as spaces. */
435 if (inCodepoint == '\t')
436 inCodepoint = ' ';
438 /* Validate character to use. */
439 if (sjme_error_is(error = sjme_scritchui_validateChar(inFont,
440 &inCodepoint)))
441 return sjme_error_default(error);
443 /* Forward. */
444 result = -1;
445 if (sjme_error_is(error = inFont->impl->pixelCharWidth(inFont,
446 inCodepoint, &result)) || result < 0)
447 return sjme_error_default(error);
449 /* Success! */
450 *outWidth = result;
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;
469 if (inCodepoint < 0)
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,
483 &inCodepoint)))
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);
491 /* Success! */
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)
504 if (inFont == NULL)
505 return SJME_ERROR_NULL_ARGUMENTS;
507 sjme_todo("Impl?");
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;
520 sjme_jchar c;
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. */
529 seqLen = -1;
530 if (sjme_error_is(error = sjme_charSeq_length(s,
531 &seqLen)) || seqLen < 0)
532 return sjme_error_default(error);
534 /* Out of bounds? */
535 if ((o + l) > seqLen)
536 return SJME_ERROR_INDEX_OUT_OF_BOUNDS;
538 /* Defaults to zero length. */
539 result = 0;
540 maxResult = 0;
542 /* Process each character within the sequence. */
543 for (at = 0; at < l; at++)
545 /* Get the next character to check. */
546 c = 0;
547 if (sjme_error_is(error = sjme_charSeq_charAt(
548 s, o + at, &c)))
549 return sjme_error_default(error);
551 /* Reset width? */
552 if (c == '\r' || c == '\n')
554 result = 0;
555 continue;
558 /* Determine character width. */
559 cw = 0;
560 if (sjme_error_is(error = inFont->api->pixelCharWidth(inFont,
561 c, &cw)))
562 return sjme_error_default(error);
564 /* Add onto. */
565 result += cw;
567 /* New max? */
568 if (result > maxResult)
569 maxResult = result;
572 /* Success! */
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;
633 /* Limit. */
634 inStyle &= SJME_SCRITCHUI_PENCIL_FONT_STYLE_ALL;
636 /* Get old font properties. */
637 wasStyle = 0;
638 wasPixelSize = 0;
639 if (sjme_error_is(error = inFont->api->metricFontStyle(inFont,
640 &wasStyle)) ||
641 sjme_error_is(error = inFont->api->metricPixelSize(inFont,
642 &wasPixelSize)))
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;
675 /* Success! */
676 return SJME_ERROR_NONE;