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 <fcntl.h>
20 #include <malloc.h>
21 #include <png.h>
22 
23 #include <android-base/logging.h>
24 
25 
26 /* Create an new empty GL texture that will be filled later */
TexWrapper()27 TexWrapper::TexWrapper() {
28     GLuint textureId;
29     glGenTextures(1, &textureId);
30     if (textureId <= 0) {
31         LOG(ERROR) << "Didn't get a texture handle allocated: " << 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. */
TexWrapper(GLuint textureId,unsigned width,unsigned height)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 
~TexWrapper()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 */
createTextureFromPng(const char * filename)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