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