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