UNPKG

17.1 kBtext/x-cView Raw
1// Copyright (c) 2010 LearnBoost <tj@learnboost.com>
2
3#include "color.h"
4
5#include <algorithm>
6#include <cmath>
7#include <cstdlib>
8#include <cstring>
9#include <limits>
10#include <map>
11#include <string>
12
13// Compatibility with Visual Studio versions prior to VS2015
14#if defined(_MSC_VER) && _MSC_VER < 1900
15#define snprintf _snprintf
16#endif
17
18/*
19 * Parse integer value
20 */
21
22template <typename parsed_t>
23static bool
24parse_integer(const char** pStr, parsed_t *pParsed) {
25 parsed_t& c = *pParsed;
26 const char*& str = *pStr;
27 int8_t sign=1;
28
29 c = 0;
30 if (*str == '-') {
31 sign=-1;
32 ++str;
33 }
34 else if (*str == '+')
35 ++str;
36
37 if (*str >= '0' && *str <= '9') {
38 do {
39 c *= 10;
40 c += *str++ - '0';
41 } while (*str >= '0' && *str <= '9');
42 } else {
43 return false;
44 }
45 if (sign<0)
46 c=-c;
47 return true;
48}
49
50
51/*
52 * Parse CSS <number> value
53 * Adapted from http://crackprogramming.blogspot.co.il/2012/10/implement-atof.html
54 */
55
56template <typename parsed_t>
57static bool
58parse_css_number(const char** pStr, parsed_t *pParsed) {
59 parsed_t &parsed = *pParsed;
60 const char*& str = *pStr;
61 const char* startStr = str;
62 if (!str || !*str)
63 return false;
64 parsed_t integerPart = 0;
65 parsed_t fractionPart = 0;
66 int divisorForFraction = 1;
67 int sign = 1;
68 int exponent = 0;
69 int digits = 0;
70 bool inFraction = false;
71
72 if (*str == '-') {
73 ++str;
74 sign = -1;
75 }
76 else if (*str == '+')
77 ++str;
78 while (*str != '\0') {
79 if (*str >= '0' && *str <= '9') {
80 if (digits>=std::numeric_limits<parsed_t>::digits10) {
81 if (!inFraction)
82 return false;
83 }
84 else {
85 ++digits;
86
87 if (inFraction) {
88 fractionPart = fractionPart*10 + (*str - '0');
89 divisorForFraction *= 10;
90 }
91 else {
92 integerPart = integerPart*10 + (*str - '0');
93 }
94 }
95 }
96 else if (*str == '.') {
97 if (inFraction)
98 break;
99 else
100 inFraction = true;
101 }
102 else if (*str == 'e') {
103 ++str;
104 if (!parse_integer(&str, &exponent))
105 return false;
106 break;
107 }
108 else
109 break;
110 ++str;
111 }
112 if (str != startStr) {
113 parsed = sign * (integerPart + fractionPart/divisorForFraction);
114 for (;exponent>0;--exponent)
115 parsed *= 10;
116 for (;exponent<0;++exponent)
117 parsed /= 10;
118 return true;
119 }
120 return false;
121}
122
123/*
124 * Clip value to the range [minValue, maxValue]
125 */
126
127template <typename T>
128static T
129clip(T value, T minValue, T maxValue) {
130 if (value > maxValue)
131 value = maxValue;
132 if (value < minValue)
133 value = minValue;
134 return value;
135}
136
137/*
138 * Wrap value to the range [0, limit]
139 */
140
141template <typename T>
142static T
143wrap_float(T value, T limit) {
144 return fmod(fmod(value, limit) + limit, limit);
145}
146
147/*
148 * Wrap value to the range [0, limit] - currently-unused integer version of wrap_float
149 */
150
151// template <typename T>
152// static T wrap_int(T value, T limit) {
153// return (value % limit + limit) % limit;
154// }
155
156/*
157 * Parse color channel value
158 */
159
160static bool
161parse_rgb_channel(const char** pStr, uint8_t *pChannel) {
162 int channel;
163 if (parse_integer(pStr, &channel)) {
164 *pChannel = clip(channel, 0, 255);
165 return true;
166 }
167 return false;
168}
169
170/*
171 * Parse a value in degrees
172 */
173
174static bool
175parse_degrees(const char** pStr, float *pDegrees) {
176 float degrees;
177 if (parse_css_number(pStr, &degrees)) {
178 *pDegrees = wrap_float(degrees, 360.0f);
179 return true;
180 }
181 return false;
182}
183
184/*
185 * Parse and clip a percentage value. Returns a float in the range [0, 1].
186 */
187
188static bool
189parse_clipped_percentage(const char** pStr, float *pFraction) {
190 float percentage;
191 bool result = parse_css_number(pStr,&percentage);
192 const char*& str = *pStr;
193 if (result) {
194 if (*str == '%') {
195 ++str;
196 *pFraction = clip(percentage, 0.0f, 100.0f) / 100.0f;
197 return result;
198 }
199 }
200 return false;
201}
202
203/*
204 * Macros to help with parsing inside rgba_from_*_string
205 */
206
207#define WHITESPACE \
208 while (' ' == *str) ++str;
209
210#define WHITESPACE_OR_COMMA \
211 while (' ' == *str || ',' == *str) ++str;
212
213#define CHANNEL(NAME) \
214 if (!parse_rgb_channel(&str, &NAME)) \
215 return 0; \
216
217#define HUE(NAME) \
218 if (!parse_degrees(&str, &NAME)) \
219 return 0;
220
221#define SATURATION(NAME) \
222 if (!parse_clipped_percentage(&str, &NAME)) \
223 return 0;
224
225#define LIGHTNESS(NAME) SATURATION(NAME)
226
227#define ALPHA(NAME) \
228 if (*str >= '1' && *str <= '9') { \
229 NAME = 1; \
230 } else { \
231 if ('0' == *str) ++str; \
232 if ('.' == *str) { \
233 ++str; \
234 float n = .1f; \
235 while (*str >= '0' && *str <= '9') { \
236 NAME += (*str++ - '0') * n; \
237 n *= .1f; \
238 } \
239 } \
240 } \
241 do {} while (0) // require trailing semicolon
242
243/*
244 * Named colors.
245 */
246static const std::map<std::string, uint32_t> named_colors = {
247 { "transparent", 0xFFFFFF00}
248 , { "aliceblue", 0xF0F8FFFF }
249 , { "antiquewhite", 0xFAEBD7FF }
250 , { "aqua", 0x00FFFFFF }
251 , { "aquamarine", 0x7FFFD4FF }
252 , { "azure", 0xF0FFFFFF }
253 , { "beige", 0xF5F5DCFF }
254 , { "bisque", 0xFFE4C4FF }
255 , { "black", 0x000000FF }
256 , { "blanchedalmond", 0xFFEBCDFF }
257 , { "blue", 0x0000FFFF }
258 , { "blueviolet", 0x8A2BE2FF }
259 , { "brown", 0xA52A2AFF }
260 , { "burlywood", 0xDEB887FF }
261 , { "cadetblue", 0x5F9EA0FF }
262 , { "chartreuse", 0x7FFF00FF }
263 , { "chocolate", 0xD2691EFF }
264 , { "coral", 0xFF7F50FF }
265 , { "cornflowerblue", 0x6495EDFF }
266 , { "cornsilk", 0xFFF8DCFF }
267 , { "crimson", 0xDC143CFF }
268 , { "cyan", 0x00FFFFFF }
269 , { "darkblue", 0x00008BFF }
270 , { "darkcyan", 0x008B8BFF }
271 , { "darkgoldenrod", 0xB8860BFF }
272 , { "darkgray", 0xA9A9A9FF }
273 , { "darkgreen", 0x006400FF }
274 , { "darkgrey", 0xA9A9A9FF }
275 , { "darkkhaki", 0xBDB76BFF }
276 , { "darkmagenta", 0x8B008BFF }
277 , { "darkolivegreen", 0x556B2FFF }
278 , { "darkorange", 0xFF8C00FF }
279 , { "darkorchid", 0x9932CCFF }
280 , { "darkred", 0x8B0000FF }
281 , { "darksalmon", 0xE9967AFF }
282 , { "darkseagreen", 0x8FBC8FFF }
283 , { "darkslateblue", 0x483D8BFF }
284 , { "darkslategray", 0x2F4F4FFF }
285 , { "darkslategrey", 0x2F4F4FFF }
286 , { "darkturquoise", 0x00CED1FF }
287 , { "darkviolet", 0x9400D3FF }
288 , { "deeppink", 0xFF1493FF }
289 , { "deepskyblue", 0x00BFFFFF }
290 , { "dimgray", 0x696969FF }
291 , { "dimgrey", 0x696969FF }
292 , { "dodgerblue", 0x1E90FFFF }
293 , { "firebrick", 0xB22222FF }
294 , { "floralwhite", 0xFFFAF0FF }
295 , { "forestgreen", 0x228B22FF }
296 , { "fuchsia", 0xFF00FFFF }
297 , { "gainsboro", 0xDCDCDCFF }
298 , { "ghostwhite", 0xF8F8FFFF }
299 , { "gold", 0xFFD700FF }
300 , { "goldenrod", 0xDAA520FF }
301 , { "gray", 0x808080FF }
302 , { "green", 0x008000FF }
303 , { "greenyellow", 0xADFF2FFF }
304 , { "grey", 0x808080FF }
305 , { "honeydew", 0xF0FFF0FF }
306 , { "hotpink", 0xFF69B4FF }
307 , { "indianred", 0xCD5C5CFF }
308 , { "indigo", 0x4B0082FF }
309 , { "ivory", 0xFFFFF0FF }
310 , { "khaki", 0xF0E68CFF }
311 , { "lavender", 0xE6E6FAFF }
312 , { "lavenderblush", 0xFFF0F5FF }
313 , { "lawngreen", 0x7CFC00FF }
314 , { "lemonchiffon", 0xFFFACDFF }
315 , { "lightblue", 0xADD8E6FF }
316 , { "lightcoral", 0xF08080FF }
317 , { "lightcyan", 0xE0FFFFFF }
318 , { "lightgoldenrodyellow", 0xFAFAD2FF }
319 , { "lightgray", 0xD3D3D3FF }
320 , { "lightgreen", 0x90EE90FF }
321 , { "lightgrey", 0xD3D3D3FF }
322 , { "lightpink", 0xFFB6C1FF }
323 , { "lightsalmon", 0xFFA07AFF }
324 , { "lightseagreen", 0x20B2AAFF }
325 , { "lightskyblue", 0x87CEFAFF }
326 , { "lightslategray", 0x778899FF }
327 , { "lightslategrey", 0x778899FF }
328 , { "lightsteelblue", 0xB0C4DEFF }
329 , { "lightyellow", 0xFFFFE0FF }
330 , { "lime", 0x00FF00FF }
331 , { "limegreen", 0x32CD32FF }
332 , { "linen", 0xFAF0E6FF }
333 , { "magenta", 0xFF00FFFF }
334 , { "maroon", 0x800000FF }
335 , { "mediumaquamarine", 0x66CDAAFF }
336 , { "mediumblue", 0x0000CDFF }
337 , { "mediumorchid", 0xBA55D3FF }
338 , { "mediumpurple", 0x9370DBFF }
339 , { "mediumseagreen", 0x3CB371FF }
340 , { "mediumslateblue", 0x7B68EEFF }
341 , { "mediumspringgreen", 0x00FA9AFF }
342 , { "mediumturquoise", 0x48D1CCFF }
343 , { "mediumvioletred", 0xC71585FF }
344 , { "midnightblue", 0x191970FF }
345 , { "mintcream", 0xF5FFFAFF }
346 , { "mistyrose", 0xFFE4E1FF }
347 , { "moccasin", 0xFFE4B5FF }
348 , { "navajowhite", 0xFFDEADFF }
349 , { "navy", 0x000080FF }
350 , { "oldlace", 0xFDF5E6FF }
351 , { "olive", 0x808000FF }
352 , { "olivedrab", 0x6B8E23FF }
353 , { "orange", 0xFFA500FF }
354 , { "orangered", 0xFF4500FF }
355 , { "orchid", 0xDA70D6FF }
356 , { "palegoldenrod", 0xEEE8AAFF }
357 , { "palegreen", 0x98FB98FF }
358 , { "paleturquoise", 0xAFEEEEFF }
359 , { "palevioletred", 0xDB7093FF }
360 , { "papayawhip", 0xFFEFD5FF }
361 , { "peachpuff", 0xFFDAB9FF }
362 , { "peru", 0xCD853FFF }
363 , { "pink", 0xFFC0CBFF }
364 , { "plum", 0xDDA0DDFF }
365 , { "powderblue", 0xB0E0E6FF }
366 , { "purple", 0x800080FF }
367 , { "rebeccapurple", 0x663399FF } // Source: CSS Color Level 4 draft
368 , { "red", 0xFF0000FF }
369 , { "rosybrown", 0xBC8F8FFF }
370 , { "royalblue", 0x4169E1FF }
371 , { "saddlebrown", 0x8B4513FF }
372 , { "salmon", 0xFA8072FF }
373 , { "sandybrown", 0xF4A460FF }
374 , { "seagreen", 0x2E8B57FF }
375 , { "seashell", 0xFFF5EEFF }
376 , { "sienna", 0xA0522DFF }
377 , { "silver", 0xC0C0C0FF }
378 , { "skyblue", 0x87CEEBFF }
379 , { "slateblue", 0x6A5ACDFF }
380 , { "slategray", 0x708090FF }
381 , { "slategrey", 0x708090FF }
382 , { "snow", 0xFFFAFAFF }
383 , { "springgreen", 0x00FF7FFF }
384 , { "steelblue", 0x4682B4FF }
385 , { "tan", 0xD2B48CFF }
386 , { "teal", 0x008080FF }
387 , { "thistle", 0xD8BFD8FF }
388 , { "tomato", 0xFF6347FF }
389 , { "turquoise", 0x40E0D0FF }
390 , { "violet", 0xEE82EEFF }
391 , { "wheat", 0xF5DEB3FF }
392 , { "white", 0xFFFFFFFF }
393 , { "whitesmoke", 0xF5F5F5FF }
394 , { "yellow", 0xFFFF00FF }
395 , { "yellowgreen", 0x9ACD32FF }
396};
397
398/*
399 * Hex digit int val.
400 */
401
402static int
403h(char c) {
404 switch (c) {
405 case '0':
406 case '1':
407 case '2':
408 case '3':
409 case '4':
410 case '5':
411 case '6':
412 case '7':
413 case '8':
414 case '9':
415 return c - '0';
416 case 'a':
417 case 'b':
418 case 'c':
419 case 'd':
420 case 'e':
421 case 'f':
422 return (c - 'a') + 10;
423 case 'A':
424 case 'B':
425 case 'C':
426 case 'D':
427 case 'E':
428 case 'F':
429 return (c - 'A') + 10;
430 }
431 return 0;
432}
433
434/*
435 * Return rgba_t from rgba.
436 */
437
438rgba_t
439rgba_create(uint32_t rgba) {
440 rgba_t color;
441 color.r = (double) (rgba >> 24) / 255;
442 color.g = (double) (rgba >> 16 & 0xff) / 255;
443 color.b = (double) (rgba >> 8 & 0xff) / 255;
444 color.a = (double) (rgba & 0xff) / 255;
445 return color;
446}
447
448/*
449 * Return a string representation of the color.
450 */
451
452void
453rgba_to_string(rgba_t rgba, char *buf, size_t len) {
454 if (1 == rgba.a) {
455 snprintf(buf, len, "#%.2x%.2x%.2x",
456 static_cast<int>(round(rgba.r * 255)),
457 static_cast<int>(round(rgba.g * 255)),
458 static_cast<int>(round(rgba.b * 255)));
459 } else {
460 snprintf(buf, len, "rgba(%d, %d, %d, %.2f)",
461 static_cast<int>(round(rgba.r * 255)),
462 static_cast<int>(round(rgba.g * 255)),
463 static_cast<int>(round(rgba.b * 255)),
464 rgba.a);
465 }
466}
467
468/*
469 * Return rgba from (r,g,b,a).
470 */
471
472static inline int32_t
473rgba_from_rgba(uint8_t r, uint8_t g, uint8_t b, uint8_t a) {
474 return
475 r << 24
476 | g << 16
477 | b << 8
478 | a;
479}
480
481/*
482 * Helper function used in rgba_from_hsla().
483 * Based on http://dev.w3.org/csswg/css-color-4/#hsl-to-rgb
484 */
485
486static float
487hue_to_rgb(float t1, float t2, float hue) {
488 if (hue < 0)
489 hue += 6;
490 if (hue >= 6)
491 hue -= 6;
492
493 if (hue < 1)
494 return (t2 - t1) * hue + t1;
495 else if (hue < 3)
496 return t2;
497 else if (hue < 4)
498 return (t2 - t1) * (4 - hue) + t1;
499 else
500 return t1;
501}
502
503/*
504 * Return rgba from (h,s,l,a).
505 * Expects h values in the range [0, 360), and s, l, a in the range [0, 1].
506 * Adapted from http://dev.w3.org/csswg/css-color-4/#hsl-to-rgb
507 */
508
509static inline int32_t
510rgba_from_hsla(float h_deg, float s, float l, float a) {
511 uint8_t r, g, b;
512 float h = (6 * h_deg) / 360.0f, m1, m2;
513
514 if (l<=0.5)
515 m2=l*(s+1);
516 else
517 m2=l+s-l*s;
518 m1 = l*2 - m2;
519
520 // Scale and round the RGB components
521 r = (uint8_t)floor(hue_to_rgb(m1, m2, h + 2) * 255 + 0.5);
522 g = (uint8_t)floor(hue_to_rgb(m1, m2, h ) * 255 + 0.5);
523 b = (uint8_t)floor(hue_to_rgb(m1, m2, h - 2) * 255 + 0.5);
524
525 return rgba_from_rgba(r, g, b, (uint8_t) (a * 255));
526}
527
528/*
529 * Return rgba from (h,s,l).
530 * Expects h values in the range [0, 360), and s, l in the range [0, 1].
531 */
532
533static inline int32_t
534rgba_from_hsl(float h_deg, float s, float l) {
535 return rgba_from_hsla(h_deg, s, l, 1.0);
536}
537
538
539/*
540 * Return rgba from (r,g,b).
541 */
542
543static int32_t
544rgba_from_rgb(uint8_t r, uint8_t g, uint8_t b) {
545 return rgba_from_rgba(r, g, b, 255);
546}
547
548/*
549 * Return rgba from #RRGGBBAA
550 */
551
552static int32_t
553rgba_from_hex8_string(const char *str) {
554 return rgba_from_rgba(
555 (h(str[0]) << 4) + h(str[1]),
556 (h(str[2]) << 4) + h(str[3]),
557 (h(str[4]) << 4) + h(str[5]),
558 (h(str[6]) << 4) + h(str[7])
559 );
560}
561
562/*
563 * Return rgb from "#RRGGBB".
564 */
565
566static int32_t
567rgba_from_hex6_string(const char *str) {
568 return rgba_from_rgb(
569 (h(str[0]) << 4) + h(str[1])
570 , (h(str[2]) << 4) + h(str[3])
571 , (h(str[4]) << 4) + h(str[5])
572 );
573}
574
575/*
576* Return rgba from #RGBA
577*/
578
579static int32_t
580rgba_from_hex4_string(const char *str) {
581 return rgba_from_rgba(
582 (h(str[0]) << 4) + h(str[0]),
583 (h(str[1]) << 4) + h(str[1]),
584 (h(str[2]) << 4) + h(str[2]),
585 (h(str[3]) << 4) + h(str[3])
586 );
587}
588
589/*
590 * Return rgb from "#RGB"
591 */
592
593static int32_t
594rgba_from_hex3_string(const char *str) {
595 return rgba_from_rgb(
596 (h(str[0]) << 4) + h(str[0])
597 , (h(str[1]) << 4) + h(str[1])
598 , (h(str[2]) << 4) + h(str[2])
599 );
600}
601
602/*
603 * Return rgb from "rgb()"
604 */
605
606static int32_t
607rgba_from_rgb_string(const char *str, short *ok) {
608 if (str == strstr(str, "rgb(")) {
609 str += 4;
610 WHITESPACE;
611 uint8_t r = 0, g = 0, b = 0;
612 CHANNEL(r);
613 WHITESPACE_OR_COMMA;
614 CHANNEL(g);
615 WHITESPACE_OR_COMMA;
616 CHANNEL(b);
617 WHITESPACE;
618 return *ok = 1, rgba_from_rgb(r, g, b);
619 }
620 return *ok = 0;
621}
622
623/*
624 * Return rgb from "rgba()"
625 */
626
627static int32_t
628rgba_from_rgba_string(const char *str, short *ok) {
629 if (str == strstr(str, "rgba(")) {
630 str += 5;
631 WHITESPACE;
632 uint8_t r = 0, g = 0, b = 0;
633 float a = 0;
634 CHANNEL(r);
635 WHITESPACE_OR_COMMA;
636 CHANNEL(g);
637 WHITESPACE_OR_COMMA;
638 CHANNEL(b);
639 WHITESPACE_OR_COMMA;
640 ALPHA(a);
641 WHITESPACE;
642 return *ok = 1, rgba_from_rgba(r, g, b, (int) (a * 255));
643 }
644 return *ok = 0;
645}
646
647/*
648 * Return rgb from "hsla()"
649 */
650
651static int32_t
652rgba_from_hsla_string(const char *str, short *ok) {
653 if (str == strstr(str, "hsla(")) {
654 str += 5;
655 WHITESPACE;
656 float h_deg = 0;
657 float s = 0, l = 0;
658 float a = 0;
659 HUE(h_deg);
660 WHITESPACE_OR_COMMA;
661 SATURATION(s);
662 WHITESPACE_OR_COMMA;
663 LIGHTNESS(l);
664 WHITESPACE_OR_COMMA;
665 ALPHA(a);
666 WHITESPACE;
667 return *ok = 1, rgba_from_hsla(h_deg, s, l, a);
668 }
669 return *ok = 0;
670}
671
672/*
673 * Return rgb from "hsl()"
674 */
675
676static int32_t
677rgba_from_hsl_string(const char *str, short *ok) {
678 if (str == strstr(str, "hsl(")) {
679 str += 4;
680 WHITESPACE;
681 float h_deg = 0;
682 float s = 0, l = 0;
683 HUE(h_deg);
684 WHITESPACE_OR_COMMA;
685 SATURATION(s);
686 WHITESPACE_OR_COMMA;
687 LIGHTNESS(l);
688 WHITESPACE;
689 return *ok = 1, rgba_from_hsl(h_deg, s, l);
690 }
691 return *ok = 0;
692}
693
694
695/*
696 * Return rgb from:
697 *
698 * - "#RGB"
699 * - "#RGBA"
700 * - "#RRGGBB"
701 * - "#RRGGBBAA"
702 *
703 */
704
705static int32_t
706rgba_from_hex_string(const char *str, short *ok) {
707 size_t len = strlen(str);
708 *ok = 1;
709 switch (len) {
710 case 8: return rgba_from_hex8_string(str);
711 case 6: return rgba_from_hex6_string(str);
712 case 4: return rgba_from_hex4_string(str);
713 case 3: return rgba_from_hex3_string(str);
714 }
715 return *ok = 0;
716}
717
718/*
719 * Return named color value.
720 */
721
722static int32_t
723rgba_from_name_string(const char *str, short *ok) {
724 std::string lowered(str);
725 std::transform(lowered.begin(), lowered.end(), lowered.begin(), tolower);
726 auto color = named_colors.find(lowered);
727 if (color != named_colors.end()) {
728 return *ok = 1, color->second;
729 }
730 return *ok = 0;
731}
732
733/*
734 * Return rgb from:
735 *
736 * - #RGB
737 * - #RGBA
738 * - #RRGGBB
739 * - #RRGGBBAA
740 * - rgb(r,g,b)
741 * - rgba(r,g,b,a)
742 * - hsl(h,s,l)
743 * - hsla(h,s,l,a)
744 * - name
745 *
746 */
747
748int32_t
749rgba_from_string(const char *str, short *ok) {
750 if ('#' == str[0])
751 return rgba_from_hex_string(++str, ok);
752 if (str == strstr(str, "rgba"))
753 return rgba_from_rgba_string(str, ok);
754 if (str == strstr(str, "rgb"))
755 return rgba_from_rgb_string(str, ok);
756 if (str == strstr(str, "hsla"))
757 return rgba_from_hsla_string(str, ok);
758 if (str == strstr(str, "hsl"))
759 return rgba_from_hsl_string(str, ok);
760 return rgba_from_name_string(str, ok);
761}
762
763/*
764 * Inspect the given rgba color.
765 */
766
767void
768rgba_inspect(int32_t rgba) {
769 printf("rgba(%d,%d,%d,%d)\n"
770 , rgba >> 24 & 0xff
771 , rgba >> 16 & 0xff
772 , rgba >> 8 & 0xff
773 , rgba & 0xff
774 );
775}