Skip to content

Fix inverted alpha for PNG textures (fully transparent pixels become opaque black) #103

Description

@Alexander-png

When loading RGBA PNG textures via gsKit_texture_png, fully transparent pixels may appear as fully opaque black in the final rendering.

This is caused by an incorrect conversion of the PNG alpha channel into the internal 7‑bit alpha representation used by gsKit. The original code inverts the alpha value, which results in transparent pixels being stored with maximum alpha.

The problematic line is located in:

  • gsKit/ee/toolkit/src/gsToolkit.c
  • Function: gsKit_texture_png
  • RGBA path (PNG_COLOR_TYPE_RGB_ALPHA)

Problematic line:

Pixels[k++].a = 128-((int) row_pointers[i][4*j+3] * 128 / 255);

This maps:

  • PNG alpha 0 (fully transparent) → 0x80 (fully opaque)
  • PNG alpha 255 (fully opaque) → ~0 (fully transparent)

As a result, with a alpha setup like:

gsGlobal->PrimAlphaEnable = GS_SETTING_ON;
gsKit_set_primalpha(gsGlobal, GS_SETREG_ALPHA(0, 1, 0, 1, 0), 0);

fully transparent pixels end up being rendered as fully opaque (typically black, depending on the texture content), which breaks sprites and UI elements that rely on transparent padding.

Proposed fix:

Replace the inverted mapping with a direct linear mapping of the PNG alpha channel from [0;255] to [0;128]:

struct pixel { u8 r,g,b,a; };
struct pixel *Pixels = (struct pixel *) Texture->Mem;

for (i = 0; i < height; i++) {
    for (j = 0; j < width; j++) {
        Pixels[k].r = row_pointers[i][4*j];
        Pixels[k].g = row_pointers[i][4*j+1];
        Pixels[k].b = row_pointers[i][4*j+2];
        Pixels[k++].a = ((int)row_pointers[i][4*j+3] * 128 + 127) / 255;  // <-- fixed line
        // Pixels[k++].a = 128-((int) row_pointers[i][4*j+3] * 128 / 255); // <-- contains bug
    }
}

This way:

  • PNG alpha 0 → internal alpha 0 (fully transparent)
  • PNG alpha 255 → internal alpha 0x80 (fully opaque)
  • Intermediate values are mapped monotonically without inversion.

Impact:

  • Fixes fully transparent pixels turning into fully opaque black.
  • Restores expected behavior for RGBA PNG textures, especially for sprites with transparent padding and UI assets.

File:

  • gsKit/ee/toolkit/src/gsToolkit.c

Reproduction:

https://github.com/Alexander-png/gsToolKit_alpha_bug_reproduce

This repository contains a minimal sample that demonstrates the issue using an RGBA PNG with transparent padding. After applying the fix above, the artifacts disappeared in that sample.

Test environment:

  • PCSX2 v2.6.3
  • BIOS SCPH-90001_V18_USA_230

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions