blob: 0e124608ea93b2ba9258ed1e0ac0396e02ef133c [file] [log] [blame]
Dees_Troy51a0e822012-09-05 15:24:24 -04001/*
2 * Copyright (C) 2007 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
17#include <stdlib.h>
18#include <unistd.h>
19
20#include <fcntl.h>
21#include <stdio.h>
22
23#include <sys/ioctl.h>
24#include <sys/mman.h>
25#include <sys/types.h>
Ethan Yonkerc798c9c2015-10-09 11:15:26 -050026#include <string.h>
Dees_Troy51a0e822012-09-05 15:24:24 -040027
28#include <linux/fb.h>
29#include <linux/kd.h>
30
31#include <pixelflinger/pixelflinger.h>
32
33#include <png.h>
Dees_Troy930bf012013-08-10 22:19:03 +000034#include "jpeglib.h"
Dees_Troy51a0e822012-09-05 15:24:24 -040035
36#include "minui.h"
37
38// libpng gives "undefined reference to 'pow'" errors, and I have no
39// idea how to convince the build system to link with -lm. We don't
40// need this functionality (it's used for gamma adjustment) so provide
41// a dummy implementation to satisfy the linker.
42double pow(double x, double y) {
43 return x;
44}
45
Ethan Yonker448c8dc2014-12-08 15:34:34 -060046#define SURFACE_DATA_ALIGNMENT 8
47
48static GGLSurface* malloc_surface(size_t data_size) {
49 unsigned char* temp = malloc(sizeof(GGLSurface) + data_size + SURFACE_DATA_ALIGNMENT);
50 if (temp == NULL) return NULL;
51 GGLSurface* surface = (GGLSurface*) temp;
52 surface->data = temp + sizeof(GGLSurface) +
53 (SURFACE_DATA_ALIGNMENT - (sizeof(GGLSurface) % SURFACE_DATA_ALIGNMENT));
54 return surface;
55}
56
57static int open_png(const char* name, png_structp* png_ptr, png_infop* info_ptr,
thatc26ba0d2015-02-13 01:09:55 +010058 png_uint_32* width, png_uint_32* height, png_byte* channels, FILE** fpp) {
Ethan Yonker448c8dc2014-12-08 15:34:34 -060059 char resPath[256];
Dees_Troy51a0e822012-09-05 15:24:24 -040060 unsigned char header[8];
Ethan Yonker448c8dc2014-12-08 15:34:34 -060061 int result = 0;
Dees_Troy51a0e822012-09-05 15:24:24 -040062
Dees Troy3454ade2015-01-20 19:21:04 +000063 snprintf(resPath, sizeof(resPath)-1, TWRES "images/%s.png", name);
64 printf("open_png %s\n", resPath);
Ethan Yonker448c8dc2014-12-08 15:34:34 -060065 resPath[sizeof(resPath)-1] = '\0';
66 FILE* fp = fopen(resPath, "rb");
Dees_Troy51a0e822012-09-05 15:24:24 -040067 if (fp == NULL) {
Ethan Yonkerac21cb52014-12-11 18:05:59 -060068 fp = fopen(name, "rb");
69 if (fp == NULL) {
70 result = -1;
71 goto exit;
72 }
Dees_Troy51a0e822012-09-05 15:24:24 -040073 }
74
75 size_t bytesRead = fread(header, 1, sizeof(header), fp);
76 if (bytesRead != sizeof(header)) {
77 result = -2;
78 goto exit;
79 }
80
81 if (png_sig_cmp(header, 0, sizeof(header))) {
82 result = -3;
83 goto exit;
84 }
85
Ethan Yonker448c8dc2014-12-08 15:34:34 -060086 *png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
87 if (!*png_ptr) {
Dees_Troy51a0e822012-09-05 15:24:24 -040088 result = -4;
89 goto exit;
90 }
91
Ethan Yonker448c8dc2014-12-08 15:34:34 -060092 *info_ptr = png_create_info_struct(*png_ptr);
93 if (!*info_ptr) {
Dees_Troy51a0e822012-09-05 15:24:24 -040094 result = -5;
95 goto exit;
96 }
97
Ethan Yonker448c8dc2014-12-08 15:34:34 -060098 if (setjmp(png_jmpbuf(*png_ptr))) {
Dees_Troy51a0e822012-09-05 15:24:24 -040099 result = -6;
100 goto exit;
101 }
102
Ethan Yonker448c8dc2014-12-08 15:34:34 -0600103 png_init_io(*png_ptr, fp);
104 png_set_sig_bytes(*png_ptr, sizeof(header));
105 png_read_info(*png_ptr, *info_ptr);
Dees_Troy51a0e822012-09-05 15:24:24 -0400106
Ethan Yonker304f32f2014-11-07 10:14:05 -0600107 int color_type, bit_depth;
Ethan Yonker448c8dc2014-12-08 15:34:34 -0600108 png_get_IHDR(*png_ptr, *info_ptr, width, height, &bit_depth,
Ethan Yonker304f32f2014-11-07 10:14:05 -0600109 &color_type, NULL, NULL, NULL);
110
Ethan Yonker448c8dc2014-12-08 15:34:34 -0600111 *channels = png_get_channels(*png_ptr, *info_ptr);
Dees_Troy51a0e822012-09-05 15:24:24 -0400112
Ethan Yonker448c8dc2014-12-08 15:34:34 -0600113 /*if (bit_depth == 8 && *channels == 3 && color_type == PNG_COLOR_TYPE_RGB) {
114 // 8-bit RGB images: great, nothing to do.
115 } else if (bit_depth <= 8 && *channels == 1 && color_type == PNG_COLOR_TYPE_GRAY) {
116 // 1-, 2-, 4-, or 8-bit gray images: expand to 8-bit gray.
117 png_set_expand_gray_1_2_4_to_8(*png_ptr);
118 } else if (bit_depth <= 8 && *channels == 1 && color_type == PNG_COLOR_TYPE_PALETTE) {
119 // paletted images: expand to 8-bit RGB. Note that we DON'T
120 // currently expand the tRNS chunk (if any) to an alpha
121 // channel, because minui doesn't support alpha channels in
122 // general.
123 png_set_palette_to_rgb(*png_ptr);
124 *channels = 3;
125 } else {
126 fprintf(stderr, "minui doesn't support PNG depth %d channels %d color_type %d\n",
127 bit_depth, *channels, color_type);
128 result = -7;
Dees_Troy51a0e822012-09-05 15:24:24 -0400129 goto exit;
Ethan Yonker448c8dc2014-12-08 15:34:34 -0600130 }*/
Dees_Troy51a0e822012-09-05 15:24:24 -0400131 if (color_type == PNG_COLOR_TYPE_PALETTE) {
132 png_set_palette_to_rgb(png_ptr);
133 }
134
thatc26ba0d2015-02-13 01:09:55 +0100135 *fpp = fp;
Ethan Yonker448c8dc2014-12-08 15:34:34 -0600136 return result;
Dees_Troy51a0e822012-09-05 15:24:24 -0400137
Ethan Yonker448c8dc2014-12-08 15:34:34 -0600138 exit:
139 if (result < 0) {
140 png_destroy_read_struct(png_ptr, info_ptr, NULL);
Dees_Troy51a0e822012-09-05 15:24:24 -0400141 }
Dees_Troy51a0e822012-09-05 15:24:24 -0400142 if (fp != NULL) {
143 fclose(fp);
144 }
Ethan Yonker448c8dc2014-12-08 15:34:34 -0600145
146 return result;
147}
148
149// "display" surfaces are transformed into the framebuffer's required
150// pixel format (currently only RGBX is supported) at load time, so
151// gr_blit() can be nothing more than a memcpy() for each row. The
152// next two functions are the only ones that know anything about the
153// framebuffer pixel format; they need to be modified if the
154// framebuffer format changes (but nothing else should).
155
156// Allocate and return a gr_surface sufficient for storing an image of
157// the indicated size in the framebuffer pixel format.
158static GGLSurface* init_display_surface(png_uint_32 width, png_uint_32 height) {
159 GGLSurface* surface;
160
161 surface = (GGLSurface*) malloc_surface(width * height * 4);
162 if (surface == NULL) return NULL;
163
164 surface->version = sizeof(GGLSurface);
165 surface->width = width;
166 surface->height = height;
167 surface->stride = width;
168
169 return surface;
170}
171
172// Copy 'input_row' to 'output_row', transforming it to the
173// framebuffer pixel format. The input format depends on the value of
174// 'channels':
175//
176// 1 - input is 8-bit grayscale
177// 3 - input is 24-bit RGB
178// 4 - input is 32-bit RGBA/RGBX
179//
180// 'width' is the number of pixels in the row.
181static void transform_rgb_to_draw(unsigned char* input_row,
182 unsigned char* output_row,
183 int channels, int width) {
184 int x;
185 unsigned char* ip = input_row;
186 unsigned char* op = output_row;
187
188 switch (channels) {
189 case 1:
190 // expand gray level to RGBX
191 for (x = 0; x < width; ++x) {
192 *op++ = *ip;
193 *op++ = *ip;
194 *op++ = *ip;
195 *op++ = 0xff;
196 ip++;
197 }
198 break;
199
200 case 3:
201 // expand RGBA to RGBX
202 for (x = 0; x < width; ++x) {
203 *op++ = *ip++;
204 *op++ = *ip++;
205 *op++ = *ip++;
206 *op++ = 0xff;
207 }
208 break;
209
210 case 4:
211 // copy RGBA to RGBX
212 memcpy(output_row, input_row, width*4);
213 break;
Dees_Troy51a0e822012-09-05 15:24:24 -0400214 }
Ethan Yonker448c8dc2014-12-08 15:34:34 -0600215}
216
217int res_create_surface_png(const char* name, gr_surface* pSurface) {
218 GGLSurface* surface = NULL;
219 int result = 0;
220 png_structp png_ptr = NULL;
221 png_infop info_ptr = NULL;
222 png_uint_32 width, height;
223 png_byte channels;
thatc26ba0d2015-02-13 01:09:55 +0100224 FILE* fp;
Ethan Yonker448c8dc2014-12-08 15:34:34 -0600225
226 *pSurface = NULL;
227
thatc26ba0d2015-02-13 01:09:55 +0100228 result = open_png(name, &png_ptr, &info_ptr, &width, &height, &channels, &fp);
Ethan Yonker448c8dc2014-12-08 15:34:34 -0600229 if (result < 0) return result;
230
231 surface = init_display_surface(width, height);
232 if (surface == NULL) {
233 result = -8;
234 goto exit;
235 }
236
237 unsigned char* p_row = malloc(width * 4);
238 unsigned int y;
239 for (y = 0; y < height; ++y) {
240 png_read_row(png_ptr, p_row, NULL);
241 transform_rgb_to_draw(p_row, surface->data + y * width * 4, channels, width);
242 }
243 free(p_row);
244
245 if (channels == 3)
246 surface->format = GGL_PIXEL_FORMAT_RGBX_8888;
247 else
248 surface->format = GGL_PIXEL_FORMAT_RGBA_8888;
249
250 *pSurface = (gr_surface) surface;
251
252 exit:
thatc26ba0d2015-02-13 01:09:55 +0100253 fclose(fp);
Ethan Yonker448c8dc2014-12-08 15:34:34 -0600254 png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
255 if (result < 0 && surface != NULL) free(surface);
Dees_Troy51a0e822012-09-05 15:24:24 -0400256 return result;
257}
258
259int res_create_surface_jpg(const char* name, gr_surface* pSurface) {
260 GGLSurface* surface = NULL;
261 int result = 0;
262 struct jpeg_decompress_struct cinfo;
263 struct jpeg_error_mgr jerr;
264
265 FILE* fp = fopen(name, "rb");
266 if (fp == NULL) {
267 char resPath[256];
268
Dees Troy3454ade2015-01-20 19:21:04 +0000269 snprintf(resPath, sizeof(resPath)-1, TWRES "images/%s", name);
Dees_Troy51a0e822012-09-05 15:24:24 -0400270 resPath[sizeof(resPath)-1] = '\0';
271 fp = fopen(resPath, "rb");
272 if (fp == NULL) {
273 result = -1;
274 goto exit;
275 }
276 }
277
278 cinfo.err = jpeg_std_error(&jerr);
279 jpeg_create_decompress(&cinfo);
280
281 /* Specify data source for decompression */
282 jpeg_stdio_src(&cinfo, fp);
283
284 /* Read file header, set default decompression parameters */
285 if (jpeg_read_header(&cinfo, TRUE) != JPEG_HEADER_OK)
286 goto exit;
287
288 /* Start decompressor */
289 (void) jpeg_start_decompress(&cinfo);
290
291 size_t width = cinfo.image_width;
292 size_t height = cinfo.image_height;
293 size_t stride = 4 * width;
294 size_t pixelSize = stride * height;
295
296 surface = malloc(sizeof(GGLSurface) + pixelSize);
297 if (surface == NULL) {
298 result = -8;
299 goto exit;
300 }
301
302 unsigned char* pData = (unsigned char*) (surface + 1);
303 surface->version = sizeof(GGLSurface);
304 surface->width = width;
305 surface->height = height;
306 surface->stride = width; /* Yes, pixels, not bytes */
307 surface->data = pData;
308 surface->format = GGL_PIXEL_FORMAT_RGBX_8888;
309
310 int y;
311 for (y = 0; y < (int) height; ++y) {
312 unsigned char* pRow = pData + y * stride;
313 jpeg_read_scanlines(&cinfo, &pRow, 1);
314
315 int x;
316 for(x = width - 1; x >= 0; x--) {
317 int sx = x * 3;
318 int dx = x * 4;
319 unsigned char r = pRow[sx];
320 unsigned char g = pRow[sx + 1];
321 unsigned char b = pRow[sx + 2];
322 unsigned char a = 0xff;
323 pRow[dx ] = r; // r
324 pRow[dx + 1] = g; // g
325 pRow[dx + 2] = b; // b
326 pRow[dx + 3] = a;
327 }
328 }
329 *pSurface = (gr_surface) surface;
330
331exit:
332 if (fp != NULL)
333 {
334 if (surface)
335 {
336 (void) jpeg_finish_decompress(&cinfo);
337 if (result < 0)
338 {
339 free(surface);
340 }
341 }
342 jpeg_destroy_decompress(&cinfo);
343 fclose(fp);
344 }
345 return result;
346}
347
348int res_create_surface(const char* name, gr_surface* pSurface) {
349 int ret;
350
351 if (!name) return -1;
352
353 if (strlen(name) > 4 && strcmp(name + strlen(name) - 4, ".jpg") == 0)
Ethan Yonker63e414f2015-02-06 15:44:39 -0600354 return res_create_surface_jpg(name,pSurface);
Dees_Troy51a0e822012-09-05 15:24:24 -0400355
356 ret = res_create_surface_png(name,pSurface);
357 if (ret < 0)
358 ret = res_create_surface_jpg(name,pSurface);
359
360 return ret;
361}
362
363void res_free_surface(gr_surface surface) {
364 GGLSurface* pSurface = (GGLSurface*) surface;
365 if (pSurface) {
366 free(pSurface);
367 }
368}
Ethan Yonker63e414f2015-02-06 15:44:39 -0600369
370// Scale image function
371int res_scale_surface(gr_surface source, gr_surface* destination, float scale_w, float scale_h) {
Ethan Yonker727aeb82015-02-10 18:07:44 -0600372 GGLContext *gl = NULL;
Ethan Yonker63e414f2015-02-06 15:44:39 -0600373 GGLSurface* sc_mem_surface = NULL;
374 *destination = NULL;
375 GGLSurface *surface = (GGLSurface*)source;
376 int w = gr_get_width(source), h = gr_get_height(source);
377 int sx = 0, sy = 0, dx = 0, dy = 0;
378 float dw = (float)w * scale_w;
379 float dh = (float)h * scale_h;
380
381 // Create a new surface that is the appropriate size
382 sc_mem_surface = init_display_surface((int)dw, (int)dh);
383 if (!sc_mem_surface) {
384 printf("gr_scale_surface failed to init_display_surface\n");
385 return -1;
386 }
387 sc_mem_surface->format = surface->format;
388
Ethan Yonker727aeb82015-02-10 18:07:44 -0600389 // Initialize the context
390 gglInit(&gl);
Ethan Yonker63e414f2015-02-06 15:44:39 -0600391 gl->colorBuffer(gl, sc_mem_surface);
392 gl->activeTexture(gl, 0);
393
394 // Enable or disable blending based on source surface format
395 if (surface->format == GGL_PIXEL_FORMAT_RGBX_8888) {
396 gl->disable(gl, GGL_BLEND);
397 } else {
398 gl->enable(gl, GGL_BLEND);
399 gl->blendFunc(gl, GGL_ONE, GGL_ZERO);
400 }
401
402 // Bind our source surface to the context
403 gl->bindTexture(gl, surface);
404
405 // Deal with the scaling
406 gl->texParameteri(gl, GGL_TEXTURE_2D, GGL_TEXTURE_MIN_FILTER, GGL_LINEAR);
407 gl->texParameteri(gl, GGL_TEXTURE_2D, GGL_TEXTURE_MAG_FILTER, GGL_LINEAR);
408 gl->texParameteri(gl, GGL_TEXTURE_2D, GGL_TEXTURE_WRAP_S, GGL_CLAMP);
409 gl->texParameteri(gl, GGL_TEXTURE_2D, GGL_TEXTURE_WRAP_T, GGL_CLAMP);
410 gl->texEnvi(gl, GGL_TEXTURE_ENV, GGL_TEXTURE_ENV_MODE, GGL_REPLACE);
411 gl->texGeni(gl, GGL_S, GGL_TEXTURE_GEN_MODE, GGL_AUTOMATIC);
412 gl->texGeni(gl, GGL_T, GGL_TEXTURE_GEN_MODE, GGL_AUTOMATIC);
413 gl->enable(gl, GGL_TEXTURE_2D);
414
415 int32_t grad[8];
416 memset(grad, 0, sizeof(grad));
417 // s, dsdx, dsdy, scale, t, dtdx, dtdy, tscale <- this is wrong!
418 // This api uses block floating-point for S and T texture coordinates.
419 // All values are given in 16.16, scaled by 'scale'. In other words,
420 // set scale to 0, for 16.16 values.
421
422 // s, dsdx, dsdy, t, dtdx, dtdy, sscale, tscale
423 float dsdx = (float)w / dw;
424 float dtdy = (float)h / dh;
425 grad[0] = ((float)sx - (dsdx * dx)) * 65536;
426 grad[1] = dsdx * 65536;
427 grad[3] = ((float)sy - (dtdy * dy)) * 65536;
428 grad[5] = dtdy * 65536;
429// printf("blit: w=%d h=%d dx=%d dy=%d dw=%f dh=%f dsdx=%f dtdy=%f s0=%x dsdx=%x t0=%x dtdy=%x\n",
430// w, h, dx, dy, dw, dh, dsdx, dtdy, grad[0], grad[1], grad[3], grad[5]);
431 gl->texCoordGradScale8xv(gl, 0 /*tmu*/, grad);
432
433 // draw / scale the source surface to our target context
434 gl->recti(gl, dx, dy, dx + dw, dy + dh);
Ethan Yonker727aeb82015-02-10 18:07:44 -0600435 gglUninit(gl);
436 gl = NULL;
Ethan Yonker63e414f2015-02-06 15:44:39 -0600437 // put the scaled surface in our destination
438 *destination = (gr_surface*) sc_mem_surface;
439 // free memory used in the source
440 res_free_surface(source);
441 source = NULL;
442 return 0;
443}