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 
18 #include "glError.h"
19 
20 #include <android-base/logging.h>
21 
22 #include <fcntl.h>
23 #include <malloc.h>
24 #include <png.h>
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 /* Wrap a texture that already allocated.  The wrapper takes ownership. */
TexWrapper(GLuint textureId,unsigned width,unsigned height)41 TexWrapper::TexWrapper(GLuint textureId, unsigned width, unsigned height) {
42     // Store the basic texture properties
43     id = textureId;
44     w = width;
45     h = height;
46 }
47 
~TexWrapper()48 TexWrapper::~TexWrapper() {
49     // Give the texture ID back
50     if (id > 0) {
51         glDeleteTextures(1, &id);
52     }
53     id = -1;
54 }
55 
56 /* Factory to build TexWrapper objects from a given PNG file */
createTextureFromPng(const char * filename)57 TexWrapper* createTextureFromPng(const char* filename) {
58     // Open the PNG file
59     FILE* inputFile = fopen(filename, "rb");
60     if (inputFile == 0) {
61         perror(filename);
62         return nullptr;
63     }
64 
65     // Read the file header and validate that it is a PNG
66     static const int kSigSize = 8;
67     png_byte header[kSigSize] = {0};
68     fread(header, 1, kSigSize, inputFile);
69     if (png_sig_cmp(header, 0, kSigSize)) {
70         printf("%s is not a PNG.\n", filename);
71         fclose(inputFile);
72         return nullptr;
73     }
74 
75     // Set up our control structure
76     png_structp pngControl = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
77     if (!pngControl) {
78         printf("png_create_read_struct failed.\n");
79         fclose(inputFile);
80         return nullptr;
81     }
82 
83     // Set up our image info structure
84     png_infop pngInfo = png_create_info_struct(pngControl);
85     if (!pngInfo) {
86         printf("error: png_create_info_struct returned 0.\n");
87         png_destroy_read_struct(&pngControl, nullptr, nullptr);
88         fclose(inputFile);
89         return nullptr;
90     }
91 
92     // Install an error handler
93     if (setjmp(png_jmpbuf(pngControl))) {
94         printf("libpng reported an error\n");
95         png_destroy_read_struct(&pngControl, &pngInfo, nullptr);
96         fclose(inputFile);
97         return nullptr;
98     }
99 
100     // Set up the png reader and fetch the remaining bits of the header
101     png_init_io(pngControl, inputFile);
102     png_set_sig_bytes(pngControl, kSigSize);
103     png_read_info(pngControl, pngInfo);
104 
105     // Get basic information about the PNG we're reading
106     int bitDepth;
107     int colorFormat;
108     png_uint_32 width;
109     png_uint_32 height;
110     png_get_IHDR(pngControl, pngInfo, &width, &height, &bitDepth, &colorFormat, NULL, NULL, NULL);
111 
112     GLint format;
113     switch (colorFormat) {
114         case PNG_COLOR_TYPE_RGB:
115             format = GL_RGB;
116             break;
117         case PNG_COLOR_TYPE_RGB_ALPHA:
118             format = GL_RGBA;
119             break;
120         default:
121             printf("%s: Unknown libpng color format %d.\n", filename, colorFormat);
122             return nullptr;
123     }
124 
125     // Refresh the values in the png info struct in case any transformation shave been applied.
126     png_read_update_info(pngControl, pngInfo);
127     int stride = png_get_rowbytes(pngControl, pngInfo);
128     stride += 3 - ((stride - 1) % 4);  // glTexImage2d requires rows to be 4-byte aligned
129 
130     // Allocate storage for the pixel data
131     png_byte* buffer = (png_byte*)malloc(stride * height);
132     if (buffer == NULL) {
133         printf("error: could not allocate memory for PNG image data\n");
134         png_destroy_read_struct(&pngControl, &pngInfo, nullptr);
135         fclose(inputFile);
136         return nullptr;
137     }
138 
139     // libpng needs an array of pointers into the image data for each row
140     png_byte** rowPointers = (png_byte**)malloc(height * sizeof(png_byte*));
141     if (rowPointers == NULL) {
142         printf("Failed to allocate temporary row pointers\n");
143         png_destroy_read_struct(&pngControl, &pngInfo, nullptr);
144         free(buffer);
145         fclose(inputFile);
146         return nullptr;
147     }
148     for (unsigned int r = 0; r < height; r++) {
149         rowPointers[r] = buffer + r * stride;
150     }
151 
152     // Read in the actual image bytes
153     png_read_image(pngControl, rowPointers);
154     png_read_end(pngControl, nullptr);
155 
156     // Set up the OpenGL texture to contain this image
157     GLuint textureId;
158     glGenTextures(1, &textureId);
159     glBindTexture(GL_TEXTURE_2D, textureId);
160 
161     // Send the image data to GL
162     glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, buffer);
163 
164     // Initialize the sampling properties (it seems the sample may not work if this isn't done)
165     // The user of this texture may very well want to set their own filtering, but we're going
166     // to pay the (minor) price of setting this up for them to avoid the dreaded "black image" if
167     // they forget.
168     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
169     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
170 
171     // clean up
172     png_destroy_read_struct(&pngControl, &pngInfo, nullptr);
173     free(buffer);
174     free(rowPointers);
175     fclose(inputFile);
176 
177     glBindTexture(GL_TEXTURE_2D, 0);
178 
179     // Return the texture
180     return new TexWrapper(textureId, width, height);
181 }
182