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