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
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 ALOGE("Didn't get a texture handle allocated: %s", 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