0
votes

Hello I am making a image and texture class for my openGL project and when I added stb_image some textures just start adding noise at the edge of the image.

Image class:

    Image::Image() {
        width = 0;
        height = 0;
        channels = 0;
        data = NULL;
    }

    Image::Image(const Image& image) {
        width = image.width;
        height = image.height;
        channels = image.channels;

        int allocation = image.width * image.height * image.channels;
        data = new unsigned char[allocation];
        std::memcpy(data, image.data, allocation);
    }

    Image& Image::operator=(const Image& image) {
        if (this != &image && image.data != nullptr) {
            if(data != NULL)
                delete[] data;
            data = NULL;

            int allocation = image.width * image.height * image.channels;
            data = new unsigned char[allocation];
            std::memcpy(data, image.data, allocation);

            width = image.width;
            height = image.height;
            channels = image.channels;
            filepath = image.filepath;
        }

        return *this;
    }

    Image::Image(const std::string& _filepath) {
        width = 0;
        height = 0;
        channels = 0;
        data = NULL;
        filepath = _filepath;

        data = stbi_load(filepath.c_str(), &width, &height, &channels, STBI_rgb_alpha);
        
        if (data == NULL) {
            Debug::systemErr("Couldn't load image: " + filepath);
        }
    }

    Image::~Image() {
        if (data != NULL) {
            delete[] data;
        }

        data = NULL;
    }

    bool Image::hasData() const{
        return (data != NULL);
    }

Texture class:

Texture::Texture() {
        textureID = 0;
    }

    Texture::Texture(const Texture& texture) {
        image = Image(texture.image);
        textureID = 0;
    }

    Texture::Texture(const std::string& path) {
        image = Image(path);
        textureID = 0;
    }

    Texture& Texture::operator=(const Texture& texture) {
        textureID = texture.textureID;
        image = texture.image;

        return *this;
    }

    Texture::~Texture() {
        destroy();
    }

    void Texture::destroy() {
        glDeleteTextures(1, &textureID);
    }

    void Texture::create() {
        if (!isCreated && image.hasData()) {

            glGenTextures(1, &textureID);
            glBindTexture(GL_TEXTURE_2D, textureID);

            //glPixelStorei(GL_UNPACK_ALIGNMENT, 1);

            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_LINEAR);
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

            glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, image.width, image.height, 0, GL_RGBA, GL_UNSIGNED_BYTE, image.data);

            glGenerateMipmap(GL_TEXTURE_2D);

            isCreated = true;
        }
    }

Image is 128x128 pixels

Here is what it looks like in paint.net:

enter image description here

Here is what it looks like when being rendered:

enter image description here

I believe the image loading is fine because I set the icon of the window with the image class and that looks fine. I think the problem stems from the texture class.

SOLVED to Jérôme Richard for solving this for me. The reason this didn't work was because even though I put rgba for the channels, the image still came back as rgb. Then when I try to copy the data, instead of doing 4 * width * height for the size. I would do 3 * width * height. So after stbi_load(), I just put channels = 4.

1
What is the value of channels after stbi_load in Image::Image? I guess this is 3 isn't it?Jérôme Richard
oh shoot your right, so i should just ignore what stb_image returns and put 4 for rgba right?gooberman420
I think yes in replacement of STBI_rgb_alpha. I also advise to add an assert just after to check the number of channels is the one you expect ;) .Jérôme Richard
ok thank you so much!gooberman420
@JérômeRichard you should post the answer and so it can be select it as accepted.Tomer W

1 Answers

1
votes

You should read stbi documentation more carefully. It's right there at the start:

   int x,y,n;
   unsigned char *data = stbi_load(filename, &x, &y, &n, 0);

... replace '0' with '1'..'4' to force that many components per pixel
... but 'n' will always be the number that it would have been if you said 0

Therefore if you pass anything other than 0 as the last argument, that's the number of channels that you will get. You should ignore n in such case and go by the number of channels you requested. Your code, on the other hand, does it the other way: it requests four channels, but then uses the returned n (in your code it's called channels) as the source of truth.