/* * Copyright (C) 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "TexWrapper.h" #include "glError.h" #include #include #include #include /* Create an new empty GL texture that will be filled later */ TexWrapper::TexWrapper() { GLuint textureId; glGenTextures(1, &textureId); if (textureId <= 0) { LOG(ERROR) << "Didn't get a texture handle allocated: " << getEGLError(); } else { // Store the basic texture properties id = textureId; w = 0; h = 0; } } /* Wrap a texture that already allocated. The wrapper takes ownership. */ TexWrapper::TexWrapper(GLuint textureId, unsigned width, unsigned height) { // Store the basic texture properties id = textureId; w = width; h = height; } TexWrapper::~TexWrapper() { // Give the texture ID back if (id > 0) { glDeleteTextures(1, &id); } id = -1; } /* Factory to build TexWrapper objects from a given PNG file */ TexWrapper* createTextureFromPng(const char * filename) { // Open the PNG file FILE *inputFile = fopen(filename, "rb"); if (inputFile == 0) { perror(filename); return nullptr; } // Read the file header and validate that it is a PNG static const int kSigSize = 8; png_byte header[kSigSize] = {0}; fread(header, 1, kSigSize, inputFile); if (png_sig_cmp(header, 0, kSigSize)) { printf("%s is not a PNG.\n", filename); fclose(inputFile); return nullptr; } // Set up our control structure png_structp pngControl = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); if (!pngControl) { printf("png_create_read_struct failed.\n"); fclose(inputFile); return nullptr; } // Set up our image info structure png_infop pngInfo = png_create_info_struct(pngControl); if (!pngInfo) { printf("error: png_create_info_struct returned 0.\n"); png_destroy_read_struct(&pngControl, nullptr, nullptr); fclose(inputFile); return nullptr; } // Install an error handler if (setjmp(png_jmpbuf(pngControl))) { printf("libpng reported an error\n"); png_destroy_read_struct(&pngControl, &pngInfo, nullptr); fclose(inputFile); return nullptr; } // Set up the png reader and fetch the remaining bits of the header png_init_io(pngControl, inputFile); png_set_sig_bytes(pngControl, kSigSize); png_read_info(pngControl, pngInfo); // Get basic information about the PNG we're reading int bitDepth; int colorFormat; png_uint_32 width; png_uint_32 height; png_get_IHDR(pngControl, pngInfo, &width, &height, &bitDepth, &colorFormat, NULL, NULL, NULL); GLint format; switch(colorFormat) { case PNG_COLOR_TYPE_RGB: format = GL_RGB; break; case PNG_COLOR_TYPE_RGB_ALPHA: format = GL_RGBA; break; default: printf("%s: Unknown libpng color format %d.\n", filename, colorFormat); return nullptr; } // Refresh the values in the png info struct in case any transformation shave been applied. png_read_update_info(pngControl, pngInfo); int stride = png_get_rowbytes(pngControl, pngInfo); stride += 3 - ((stride-1) % 4); // glTexImage2d requires rows to be 4-byte aligned // Allocate storage for the pixel data png_byte * buffer = (png_byte*)malloc(stride * height); if (buffer == NULL) { printf("error: could not allocate memory for PNG image data\n"); png_destroy_read_struct(&pngControl, &pngInfo, nullptr); fclose(inputFile); return nullptr; } // libpng needs an array of pointers into the image data for each row png_byte ** rowPointers = (png_byte**)malloc(height * sizeof(png_byte*)); if (rowPointers == NULL) { printf("Failed to allocate temporary row pointers\n"); png_destroy_read_struct(&pngControl, &pngInfo, nullptr); free(buffer); fclose(inputFile); return nullptr; } for (unsigned int r = 0; r < height; r++) { rowPointers[r] = buffer + r*stride; } // Read in the actual image bytes png_read_image(pngControl, rowPointers); png_read_end(pngControl, nullptr); // Set up the OpenGL texture to contain this image GLuint textureId; glGenTextures(1, &textureId); glBindTexture(GL_TEXTURE_2D, textureId); // Send the image data to GL glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, buffer); // Initialize the sampling properties (it seems the sample may not work if this isn't done) // The user of this texture may very well want to set their own filtering, but we're going // to pay the (minor) price of setting this up for them to avoid the dreaded "black image" if // they forget. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); // clean up png_destroy_read_struct(&pngControl, &pngInfo, nullptr); free(buffer); free(rowPointers); fclose(inputFile); glBindTexture(GL_TEXTURE_2D, 0); // Return the texture return new TexWrapper(textureId, width, height); }