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