@@ -332,6 +332,217 @@ void gbafilter_pal32(uint32_t* buf, int count)
332332 final_pix |= (final_red_8bit & 0x07 ) << (systemRedShift - 3 );
333333
334334
335+ // Green component
336+ // 5 MSBs for the 'systemGreenShift' position
337+ final_pix |= ((final_green_8bit >> 3 ) & 0x1f ) << systemGreenShift;
338+ // 3 LSBs for the 'base' position (systemGreenShift - 3)
339+ final_pix |= (final_green_8bit & 0x07 ) << (systemGreenShift - 3 );
340+
341+ // Blue component
342+ // 5 MSBs for the 'systemBlueShift' position
343+ final_pix |= ((final_blue_8bit >> 3 ) & 0x1f ) << systemBlueShift;
344+ // 3 LSBs for the 'base' position (systemBlueShift - 3)
345+ final_pix |= (final_blue_8bit & 0x07 ) << (systemBlueShift - 3 );
346+
347+ // Preserve existing alpha if present (assuming it's at bits 24-31 for 32-bit depth)
348+ if (systemColorDepth == 32 ) {
349+ final_pix |= (pix & (0xFF << 24 ));
350+ }
351+
352+ *buf++ = final_pix;
353+ }
354+ }
355+
356+ void gbafilter_update_colors_native (bool lcd) {
357+ switch (systemColorDepth) {
358+ case 8 : {
359+ for (int i = 0 ; i < 0x10000 ; i++) {
360+ systemColorMap8[i] = (uint8_t )((((i & 0x1f ) << 3 ) & 0xE0 ) |
361+ ((((i & 0x3e0 ) >> 5 ) << 0 ) & 0x1C ) |
362+ ((((i & 0x7c00 ) >> 10 ) >> 3 ) & 0x3 ));
363+ }
364+ if (lcd)
365+ gbafilter_pal8 (systemColorMap8, 0x10000 );
366+ } break ;
367+ case 16 : {
368+ #ifdef FRONTEND_SUPPORTS_BGR1555
369+ for (int i = 0x0 ; i < 0x10000 ; i++) {
370+ // GBA uses BGR555 format: 0BBBBBGGGGGRRRRR
371+ // Red: bits 0-4, Green: bits 5-9, Blue: bits 10-14
372+ systemColorMap16[i] = ((i & 0x1f ) << systemRedShift) |
373+ (((i & 0x3e0 ) >> 5 ) << systemGreenShift) |
374+ (((i & 0x7c00 ) >> 10 ) << systemBlueShift);
375+ }
376+ if (lcd)
377+ gbafilter_pal (systemColorMap16, 0x10000 );
378+ #else
379+ for (int i = 0x0 ; i < 0x10000 ; i++) {
380+ // GBA uses BGR555 format: 0BBBBBGGGGGRRRRR
381+ // Red: bits 0-4, Green: bits 5-9, Blue: bits 10-14
382+ // GB/GBC uses BGR555 format: 0BBBBBGGGGGRRRRR
383+ // Red: bits 0-4, Green: bits 5-9, Blue: bits 10-14
384+ int r5 = i & 0x1F ;
385+ int g5 = (i >> 5 ) & 0x1F ;
386+ int b5 = (i >> 10 ) & 0x1F ;
387+
388+ // Map to 16-bit RGB565 (5-6-5)
389+ int g6 = (g5 << 1 ) | (g5 >> 4 );
390+
391+ systemColorMap16[i] =
392+ (r5 << systemRedShift) |
393+ (g6 << (systemGreenShift - 1 )) |
394+ (b5 << systemBlueShift);
395+ }
396+ if (lcd)
397+ gbafilter_pal_565 (systemColorMap16, 0x10000 );
398+ #endif
399+ } break ;
400+ case 24 :
401+ case 32 : {
402+ for (int i = 0 ; i < 0x10000 ; i++) {
403+ // GBA uses BGR555 format: 0BBBBBGGGGGRRRRR
404+ // Red: bits 0-4, Green: bits 5-9, Blue: bits 10-14
405+ int r5 = i & 0x1F ;
406+ int g5 = (i >> 5 ) & 0x1F ;
407+ int b5 = (i >> 10 ) & 0x1F ;
408+
409+ // Expand 5-bit to 8-bit components
410+ uint8_t final_red_8bit = (r5 << 3 ) | (r5 >> 2 );
411+ uint8_t final_green_8bit = (g5 << 3 ) | (g5 >> 2 );
412+ uint8_t final_blue_8bit = (b5 << 3 ) | (b5 >> 2 );
413+
414+ uint32_t final_pix = 0 ;
415+
416+ // Red component
417+ // 5 most significant bits (MSBs) for the 'systemRedShift' position
418+ final_pix |= ((final_red_8bit >> 3 ) & 0x1f ) << systemRedShift;
419+ // 3 least significant bits (LSBs) for the 'base' position (systemRedShift - 3)
420+ final_pix |= (final_red_8bit & 0x07 ) << (systemRedShift - 3 );
421+
422+ // Green component
423+ // 5 MSBs for the 'systemGreenShift' position
424+ final_pix |= ((final_green_8bit >> 3 ) & 0x1f ) << systemGreenShift;
425+ // 3 LSBs for the 'base' position (systemGreenShift - 3)
426+ final_pix |= (final_green_8bit & 0x07 ) << (systemGreenShift - 3 );
427+
428+ // Blue component
429+ // 5 MSBs for the 'systemBlueShift' position
430+ final_pix |= ((final_blue_8bit >> 3 ) & 0x1f ) << systemBlueShift;
431+ // 3 LSBs for the 'base' position (systemBlueShift - 3)
432+ final_pix |= (final_blue_8bit & 0x07 ) << (systemBlueShift - 3 );
433+
434+ systemColorMap32[i] = final_pix;
435+ }
436+ if (lcd)
437+ gbafilter_pal_888 (systemColorMap32, 0x10000 );
438+ } break ;
439+ }
440+ }
441+
442+ #define APPLY_FILTER (r, g, b ) \
443+ do { \
444+ /* 1. Apply initial gamma (including darken_screen as exponent) to convert to linear space. */ \
445+ /* This step will affect non-"white" values. */ \
446+ r = powf (r, target_gamma_exponent); \
447+ g = powf (g, target_gamma_exponent); \
448+ b = powf (b, target_gamma_exponent); \
449+ /* 2. Apply luminance factor and clamp. */ \
450+ r = fmaxf (0 .0f , fminf (1 .0f , r * luminance_factor)); \
451+ g = fmaxf (0 .0f , fminf (1 .0f , g * luminance_factor)); \
452+ b = fmaxf (0 .0f , fminf (1 .0f , b * luminance_factor)); \
453+ /* 3. Apply color profile matrix (using profile[column][row] access) */ \
454+ transformed_r = profile[0 ][0 ] * r + profile[1 ][0 ] * g + profile[2 ][0 ] * b; \
455+ transformed_g = profile[0 ][1 ] * r + profile[1 ][1 ] * g + profile[2 ][1 ] * b; \
456+ transformed_b = profile[0 ][2 ] * r + profile[1 ][2 ] * g + profile[2 ][2 ] * b; \
457+ /* 4. Apply display gamma to convert back for display. */ \
458+ transformed_r = copysignf (powf (fabsf (transformed_r), display_gamma_reciprocal), transformed_r); \
459+ transformed_g = copysignf (powf (fabsf (transformed_g), display_gamma_reciprocal), transformed_g); \
460+ transformed_b = copysignf (powf (fabsf (transformed_b), display_gamma_reciprocal), transformed_b); \
461+ /* Final clamp: ensure values are within 0.0-1.0 range */ \
462+ transformed_r = fmaxf (0 .0f , fminf (1 .0f , transformed_r)); \
463+ transformed_g = fmaxf (0 .0f , fminf (1 .0f , transformed_g)); \
464+ transformed_b = fmaxf (0 .0f , fminf (1 .0f , transformed_b)); \
465+ } while (0 )
466+
467+
468+ void gbafilter_pal_565 (uint16_t * buf, int count)
469+ {
470+ // Pre-calculate constants for efficiency within function scope
471+ const float target_gamma_exponent = target_gamma + darken_screen;
472+ const float display_gamma_reciprocal = 1 .0f / display_gamma;
473+ const float luminance_factor = profile[3 ][3 ]; // profile[3].w from GLSL
474+
475+ while (count--) {
476+ float transformed_r, transformed_g, transformed_b;
477+ uint16_t pix = *buf;
478+
479+ uint8_t original_r_val_5bit = (uint8_t )((pix >> systemRedShift) & 0x1f );
480+ uint8_t original_g_val_6bit = (uint8_t )((pix >> (systemGreenShift - 1 )) & 0x3f );
481+ uint8_t original_b_val_5bit = (uint8_t )((pix >> systemBlueShift) & 0x1f );
482+
483+ // Normalize to 0.0-1.0 for calculations
484+ float r = (float )original_r_val_5bit / 31 .0f ;
485+ float g = (float )original_g_val_6bit / 63 .0f ;
486+ float b = (float )original_b_val_5bit / 31 .0f ;
487+
488+ APPLY_FILTER (r, g, b);
489+
490+ // Convert back to 5-bit (0-31) integer and combine into uint16_t
491+ // Apply 5-bit to 5-bit conversion, as this palette is for 16-bit output.
492+ uint8_t final_red = (uint8_t )(transformed_r * 31 .0f + 0 .5f );
493+ uint8_t final_green = (uint8_t )(transformed_g * 63 .0f + 0 .5f );
494+ uint8_t final_blue = (uint8_t )(transformed_b * 31 .0f + 0 .5f );
495+
496+ // Ensure values are strictly within 0-31 range after rounding
497+ if (final_red > 31 ) final_red = 31 ;
498+ if (final_green > 63 ) final_green = 63 ;
499+ if (final_blue > 31 ) final_blue = 31 ;
500+
501+ *buf++ = (final_red << systemRedShift) |
502+ (final_green << (systemGreenShift - 1 )) |
503+ (final_blue << systemBlueShift);
504+ }
505+ }
506+
507+ void gbafilter_pal_888 (uint32_t * buf, int count)
508+ {
509+ // Pre-calculate constants for efficiency within function scope
510+ const float target_gamma_exponent = target_gamma + darken_screen;
511+ const float display_gamma_reciprocal = 1 .0f / display_gamma;
512+ const float luminance_factor = profile[3 ][3 ]; // profile[3].w from GLSL
513+
514+ while (count--) {
515+ float transformed_r, transformed_g, transformed_b;
516+ uint32_t pix = *buf;
517+
518+ // Extract original 5-bit R, G, B values from the shifted positions in the 32-bit pixel.
519+ // These shifts pull out the 5-bit value from its shifted position (e.g., bits 3-7 for Red).
520+ uint8_t original_r_val_8bit = (uint8_t )((pix >> (systemRedShift - 3 )) & 0xff );
521+ uint8_t original_g_val_8bit = (uint8_t )((pix >> (systemGreenShift - 3 )) & 0xff );
522+ uint8_t original_b_val_8bit = (uint8_t )((pix >> (systemBlueShift - 3 )) & 0xff );
523+
524+ // Normalize to 0.0-1.0 for calculations
525+ float r = (float )original_r_val_8bit / 255 .0f ;
526+ float g = (float )original_g_val_8bit / 255 .0f ;
527+ float b = (float )original_b_val_8bit / 255 .0f ;
528+
529+ APPLY_FILTER (r, g, b);
530+
531+ // Convert the floating-point values to 8-bit integer components (0-255).
532+ // Values are already guaranteed to be in 0-255 range since they are uint8_t
533+ // and the floating point values are clamped to 0.0-1.0 before conversion.
534+ uint8_t final_red_8bit = (uint8_t )(transformed_r * 255 .0f + 0 .5f );
535+ uint8_t final_green_8bit = (uint8_t )(transformed_g * 255 .0f + 0 .5f );
536+ uint8_t final_blue_8bit = (uint8_t )(transformed_b * 255 .0f + 0 .5f );
537+
538+ uint32_t final_pix = 0 ;
539+
540+ // Red component
541+ // 5 most significant bits (MSBs) for the 'systemRedShift' position
542+ final_pix |= ((final_red_8bit >> 3 ) & 0x1f ) << systemRedShift;
543+ // 3 least significant bits (LSBs) for the 'base' position (systemRedShift - 3)
544+ final_pix |= (final_red_8bit & 0x07 ) << (systemRedShift - 3 );
545+
335546 // Green component
336547 // 5 MSBs for the 'systemGreenShift' position
337548 final_pix |= ((final_green_8bit >> 3 ) & 0x1f ) << systemGreenShift;
0 commit comments