1/* 2 * Copyright (C) 2017 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16#include "TexWrapper.h" 17#include "glError.h" 18 19#include "log/log.h" 20 21#include <fcntl.h> 22#include <malloc.h> 23#include <png.h> 24 25 26/* Create an new empty GL texture that will be filled later */ 27TexWrapper::TexWrapper() { 28 GLuint textureId; 29 glGenTextures(1, &textureId); 30 if (textureId <= 0) { 31 ALOGE("Didn't get a texture handle allocated: %s", getEGLError()); 32 } else { 33 // Store the basic texture properties 34 id = textureId; 35 w = 0; 36 h = 0; 37 } 38} 39 40 41/* Wrap a texture that already allocated. The wrapper takes ownership. */ 42TexWrapper::TexWrapper(GLuint textureId, unsigned width, unsigned height) { 43 // Store the basic texture properties 44 id = textureId; 45 w = width; 46 h = height; 47} 48 49 50TexWrapper::~TexWrapper() { 51 // Give the texture ID back 52 if (id > 0) { 53 glDeleteTextures(1, &id); 54 } 55 id = -1; 56} 57 58 59/* Factory to build TexWrapper objects from a given PNG file */ 60TexWrapper* createTextureFromPng(const char * filename) 61{ 62 // Open the PNG file 63 FILE *inputFile = fopen(filename, "rb"); 64 if (inputFile == 0) 65 { 66 perror(filename); 67 return nullptr; 68 } 69 70 // Read the file header and validate that it is a PNG 71 static const int kSigSize = 8; 72 png_byte header[kSigSize] = {0}; 73 fread(header, 1, kSigSize, inputFile); 74 if (png_sig_cmp(header, 0, kSigSize)) { 75 printf("%s is not a PNG.\n", filename); 76 fclose(inputFile); 77 return nullptr; 78 } 79 80 // Set up our control structure 81 png_structp pngControl = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); 82 if (!pngControl) 83 { 84 printf("png_create_read_struct failed.\n"); 85 fclose(inputFile); 86 return nullptr; 87 } 88 89 // Set up our image info structure 90 png_infop pngInfo = png_create_info_struct(pngControl); 91 if (!pngInfo) 92 { 93 printf("error: png_create_info_struct returned 0.\n"); 94 png_destroy_read_struct(&pngControl, nullptr, nullptr); 95 fclose(inputFile); 96 return nullptr; 97 } 98 99 // Install an error handler 100 if (setjmp(png_jmpbuf(pngControl))) { 101 printf("libpng reported an error\n"); 102 png_destroy_read_struct(&pngControl, &pngInfo, nullptr); 103 fclose(inputFile); 104 return nullptr; 105 } 106 107 // Set up the png reader and fetch the remaining bits of the header 108 png_init_io(pngControl, inputFile); 109 png_set_sig_bytes(pngControl, kSigSize); 110 png_read_info(pngControl, pngInfo); 111 112 // Get basic information about the PNG we're reading 113 int bitDepth; 114 int colorFormat; 115 png_uint_32 width; 116 png_uint_32 height; 117 png_get_IHDR(pngControl, pngInfo, 118 &width, &height, 119 &bitDepth, &colorFormat, 120 NULL, NULL, NULL); 121 122 GLint format; 123 switch(colorFormat) 124 { 125 case PNG_COLOR_TYPE_RGB: 126 format = GL_RGB; 127 break; 128 case PNG_COLOR_TYPE_RGB_ALPHA: 129 format = GL_RGBA; 130 break; 131 default: 132 printf("%s: Unknown libpng color format %d.\n", filename, colorFormat); 133 return nullptr; 134 } 135 136 // Refresh the values in the png info struct in case any transformation shave been applied. 137 png_read_update_info(pngControl, pngInfo); 138 int stride = png_get_rowbytes(pngControl, pngInfo); 139 stride += 3 - ((stride-1) % 4); // glTexImage2d requires rows to be 4-byte aligned 140 141 // Allocate storage for the pixel data 142 png_byte * buffer = (png_byte*)malloc(stride * height); 143 if (buffer == NULL) 144 { 145 printf("error: could not allocate memory for PNG image data\n"); 146 png_destroy_read_struct(&pngControl, &pngInfo, nullptr); 147 fclose(inputFile); 148 return nullptr; 149 } 150 151 // libpng needs an array of pointers into the image data for each row 152 png_byte ** rowPointers = (png_byte**)malloc(height * sizeof(png_byte*)); 153 if (rowPointers == NULL) 154 { 155 printf("Failed to allocate temporary row pointers\n"); 156 png_destroy_read_struct(&pngControl, &pngInfo, nullptr); 157 free(buffer); 158 fclose(inputFile); 159 return nullptr; 160 } 161 for (unsigned int r = 0; r < height; r++) 162 { 163 rowPointers[r] = buffer + r*stride; 164 } 165 166 167 // Read in the actual image bytes 168 png_read_image(pngControl, rowPointers); 169 png_read_end(pngControl, nullptr); 170 171 172 // Set up the OpenGL texture to contain this image 173 GLuint textureId; 174 glGenTextures(1, &textureId); 175 glBindTexture(GL_TEXTURE_2D, textureId); 176 177 // Send the image data to GL 178 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, buffer); 179 180 // Initialize the sampling properties (it seems the sample may not work if this isn't done) 181 // The user of this texture may very well want to set their own filtering, but we're going 182 // to pay the (minor) price of setting this up for them to avoid the dreaded "black image" if 183 // they forget. 184 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); 185 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); 186 187 // clean up 188 png_destroy_read_struct(&pngControl, &pngInfo, nullptr); 189 free(buffer); 190 free(rowPointers); 191 fclose(inputFile); 192 193 glBindTexture(GL_TEXTURE_2D, 0); 194 195 196 // Return the texture 197 return new TexWrapper(textureId, width, height); 198} 199