Home | History | Annotate | Download | only in app
      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 */
     27 TexWrapper::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. */
     42 TexWrapper::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 
     50 TexWrapper::~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 */
     60 TexWrapper* 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