blob: 42e3131c4281e44a5cba09bc8ceb683f9202b669 [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>
Ethan Yonkerfbb43532015-12-28 21:54:50 +010018#include <string.h>
Dees_Troy51a0e822012-09-05 15:24:24 -040019#include <unistd.h>
20
21#include <fcntl.h>
22#include <stdio.h>
23
24#include <sys/ioctl.h>
25#include <sys/mman.h>
26#include <sys/types.h>
27
28#include <linux/fb.h>
29#include <linux/kd.h>
30
Dees_Troy51a0e822012-09-05 15:24:24 -040031#include <png.h>
Ethan Yonkerfbb43532015-12-28 21:54:50 +010032
33#include <pixelflinger/pixelflinger.h>
Ethan Yonkere95c4862016-01-27 10:57:26 -060034#ifdef TW_INCLUDE_JPEG
Ethan Yonkerfbb43532015-12-28 21:54:50 +010035extern "C" {
Dees_Troy930bf012013-08-10 22:19:03 +000036#include "jpeglib.h"
Dees_Troy51a0e822012-09-05 15:24:24 -040037}
Ethan Yonkere95c4862016-01-27 10:57:26 -060038#endif
bigbiffd81833a2021-01-17 11:06:57 -050039#include "minuitwrp/minui.h"
Dees_Troy51a0e822012-09-05 15:24:24 -040040
Ethan Yonker448c8dc2014-12-08 15:34:34 -060041#define SURFACE_DATA_ALIGNMENT 8
42
43static GGLSurface* malloc_surface(size_t data_size) {
Ethan Yonkerfbb43532015-12-28 21:54:50 +010044 size_t size = sizeof(GGLSurface) + data_size + SURFACE_DATA_ALIGNMENT;
45 unsigned char* temp = reinterpret_cast<unsigned char*>(malloc(size));
Ethan Yonker448c8dc2014-12-08 15:34:34 -060046 if (temp == NULL) return NULL;
Ethan Yonkerfbb43532015-12-28 21:54:50 +010047 GGLSurface* surface = reinterpret_cast<GGLSurface*>(temp);
Ethan Yonker448c8dc2014-12-08 15:34:34 -060048 surface->data = temp + sizeof(GGLSurface) +
49 (SURFACE_DATA_ALIGNMENT - (sizeof(GGLSurface) % SURFACE_DATA_ALIGNMENT));
50 return surface;
51}
52
53static int open_png(const char* name, png_structp* png_ptr, png_infop* info_ptr,
thatc26ba0d2015-02-13 01:09:55 +010054 png_uint_32* width, png_uint_32* height, png_byte* channels, FILE** fpp) {
Ethan Yonker448c8dc2014-12-08 15:34:34 -060055 char resPath[256];
Dees_Troy51a0e822012-09-05 15:24:24 -040056 unsigned char header[8];
Ethan Yonker448c8dc2014-12-08 15:34:34 -060057 int result = 0;
Ethan Yonkerfbb43532015-12-28 21:54:50 +010058 int color_type, bit_depth;
59 size_t bytesRead;
Dees_Troy51a0e822012-09-05 15:24:24 -040060
Dees Troy3454ade2015-01-20 19:21:04 +000061 snprintf(resPath, sizeof(resPath)-1, TWRES "images/%s.png", name);
Ethan Yonker448c8dc2014-12-08 15:34:34 -060062 resPath[sizeof(resPath)-1] = '\0';
63 FILE* fp = fopen(resPath, "rb");
Dees_Troy51a0e822012-09-05 15:24:24 -040064 if (fp == NULL) {
Ethan Yonkerac21cb52014-12-11 18:05:59 -060065 fp = fopen(name, "rb");
66 if (fp == NULL) {
67 result = -1;
68 goto exit;
69 }
Dees_Troy51a0e822012-09-05 15:24:24 -040070 }
71
Ethan Yonkerfbb43532015-12-28 21:54:50 +010072 bytesRead = fread(header, 1, sizeof(header), fp);
Dees_Troy51a0e822012-09-05 15:24:24 -040073 if (bytesRead != sizeof(header)) {
74 result = -2;
75 goto exit;
76 }
77
78 if (png_sig_cmp(header, 0, sizeof(header))) {
79 result = -3;
80 goto exit;
81 }
82
Ethan Yonker448c8dc2014-12-08 15:34:34 -060083 *png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
84 if (!*png_ptr) {
Dees_Troy51a0e822012-09-05 15:24:24 -040085 result = -4;
86 goto exit;
87 }
88
Ethan Yonker448c8dc2014-12-08 15:34:34 -060089 *info_ptr = png_create_info_struct(*png_ptr);
90 if (!*info_ptr) {
Dees_Troy51a0e822012-09-05 15:24:24 -040091 result = -5;
92 goto exit;
93 }
94
Ethan Yonker448c8dc2014-12-08 15:34:34 -060095 if (setjmp(png_jmpbuf(*png_ptr))) {
Dees_Troy51a0e822012-09-05 15:24:24 -040096 result = -6;
97 goto exit;
98 }
99
Ethan Yonker448c8dc2014-12-08 15:34:34 -0600100 png_init_io(*png_ptr, fp);
101 png_set_sig_bytes(*png_ptr, sizeof(header));
102 png_read_info(*png_ptr, *info_ptr);
Dees_Troy51a0e822012-09-05 15:24:24 -0400103
Ethan Yonker448c8dc2014-12-08 15:34:34 -0600104 png_get_IHDR(*png_ptr, *info_ptr, width, height, &bit_depth,
Ethan Yonker304f32f2014-11-07 10:14:05 -0600105 &color_type, NULL, NULL, NULL);
106
Ethan Yonker448c8dc2014-12-08 15:34:34 -0600107 *channels = png_get_channels(*png_ptr, *info_ptr);
Dees_Troy51a0e822012-09-05 15:24:24 -0400108
Ethan Yonkerfbb43532015-12-28 21:54:50 +0100109 if (bit_depth == 8 && *channels == 3 && color_type == PNG_COLOR_TYPE_RGB) {
Ethan Yonker448c8dc2014-12-08 15:34:34 -0600110 // 8-bit RGB images: great, nothing to do.
111 } else if (bit_depth <= 8 && *channels == 1 && color_type == PNG_COLOR_TYPE_GRAY) {
112 // 1-, 2-, 4-, or 8-bit gray images: expand to 8-bit gray.
113 png_set_expand_gray_1_2_4_to_8(*png_ptr);
114 } else if (bit_depth <= 8 && *channels == 1 && color_type == PNG_COLOR_TYPE_PALETTE) {
115 // paletted images: expand to 8-bit RGB. Note that we DON'T
116 // currently expand the tRNS chunk (if any) to an alpha
117 // channel, because minui doesn't support alpha channels in
118 // general.
119 png_set_palette_to_rgb(*png_ptr);
120 *channels = 3;
Ethan Yonkerfbb43532015-12-28 21:54:50 +0100121 } else if (color_type == PNG_COLOR_TYPE_PALETTE) {
122 png_set_palette_to_rgb(*png_ptr);
Dees_Troy51a0e822012-09-05 15:24:24 -0400123 }
124
thatc26ba0d2015-02-13 01:09:55 +0100125 *fpp = fp;
Ethan Yonker448c8dc2014-12-08 15:34:34 -0600126 return result;
Dees_Troy51a0e822012-09-05 15:24:24 -0400127
Ethan Yonker448c8dc2014-12-08 15:34:34 -0600128 exit:
129 if (result < 0) {
130 png_destroy_read_struct(png_ptr, info_ptr, NULL);
Dees_Troy51a0e822012-09-05 15:24:24 -0400131 }
Dees_Troy51a0e822012-09-05 15:24:24 -0400132 if (fp != NULL) {
133 fclose(fp);
134 }
Ethan Yonker448c8dc2014-12-08 15:34:34 -0600135
136 return result;
137}
138
139// "display" surfaces are transformed into the framebuffer's required
140// pixel format (currently only RGBX is supported) at load time, so
141// gr_blit() can be nothing more than a memcpy() for each row. The
142// next two functions are the only ones that know anything about the
143// framebuffer pixel format; they need to be modified if the
144// framebuffer format changes (but nothing else should).
145
Ethan Yonkerfbb43532015-12-28 21:54:50 +0100146// Allocate and return a GRSurface* sufficient for storing an image of
Ethan Yonker448c8dc2014-12-08 15:34:34 -0600147// the indicated size in the framebuffer pixel format.
148static GGLSurface* init_display_surface(png_uint_32 width, png_uint_32 height) {
Ethan Yonkerfbb43532015-12-28 21:54:50 +0100149 GGLSurface* surface = malloc_surface(width * height * 4);
Ethan Yonker448c8dc2014-12-08 15:34:34 -0600150 if (surface == NULL) return NULL;
151
152 surface->version = sizeof(GGLSurface);
153 surface->width = width;
154 surface->height = height;
155 surface->stride = width;
156
157 return surface;
158}
159
160// Copy 'input_row' to 'output_row', transforming it to the
161// framebuffer pixel format. The input format depends on the value of
162// 'channels':
163//
164// 1 - input is 8-bit grayscale
165// 3 - input is 24-bit RGB
166// 4 - input is 32-bit RGBA/RGBX
167//
168// 'width' is the number of pixels in the row.
169static void transform_rgb_to_draw(unsigned char* input_row,
170 unsigned char* output_row,
171 int channels, int width) {
172 int x;
173 unsigned char* ip = input_row;
174 unsigned char* op = output_row;
175
176 switch (channels) {
177 case 1:
178 // expand gray level to RGBX
179 for (x = 0; x < width; ++x) {
180 *op++ = *ip;
181 *op++ = *ip;
182 *op++ = *ip;
183 *op++ = 0xff;
184 ip++;
185 }
186 break;
187
188 case 3:
189 // expand RGBA to RGBX
190 for (x = 0; x < width; ++x) {
191 *op++ = *ip++;
192 *op++ = *ip++;
193 *op++ = *ip++;
194 *op++ = 0xff;
195 }
196 break;
197
198 case 4:
199 // copy RGBA to RGBX
200 memcpy(output_row, input_row, width*4);
201 break;
Dees_Troy51a0e822012-09-05 15:24:24 -0400202 }
Ethan Yonker448c8dc2014-12-08 15:34:34 -0600203}
204
205int res_create_surface_png(const char* name, gr_surface* pSurface) {
206 GGLSurface* surface = NULL;
207 int result = 0;
208 png_structp png_ptr = NULL;
209 png_infop info_ptr = NULL;
210 png_uint_32 width, height;
211 png_byte channels;
thatc26ba0d2015-02-13 01:09:55 +0100212 FILE* fp;
Ethan Yonkerfbb43532015-12-28 21:54:50 +0100213 unsigned char* p_row;
214 unsigned int y;
Ethan Yonker448c8dc2014-12-08 15:34:34 -0600215
216 *pSurface = NULL;
217
thatc26ba0d2015-02-13 01:09:55 +0100218 result = open_png(name, &png_ptr, &info_ptr, &width, &height, &channels, &fp);
Ethan Yonker448c8dc2014-12-08 15:34:34 -0600219 if (result < 0) return result;
220
221 surface = init_display_surface(width, height);
222 if (surface == NULL) {
223 result = -8;
224 goto exit;
225 }
226
bigbiffe4a84df2022-04-10 14:47:39 -0400227#if defined(RECOVERY_ARGB) || defined(RECOVERY_BGRA)
Ethan Yonkerfbb43532015-12-28 21:54:50 +0100228 png_set_bgr(png_ptr);
229#endif
230
231 p_row = reinterpret_cast<unsigned char*>(malloc(width * 4));
232 if (p_row == NULL) {
233 result = -9;
234 goto exit;
235 }
Ethan Yonker448c8dc2014-12-08 15:34:34 -0600236 for (y = 0; y < height; ++y) {
237 png_read_row(png_ptr, p_row, NULL);
238 transform_rgb_to_draw(p_row, surface->data + y * width * 4, channels, width);
239 }
240 free(p_row);
241
242 if (channels == 3)
243 surface->format = GGL_PIXEL_FORMAT_RGBX_8888;
244 else
245 surface->format = GGL_PIXEL_FORMAT_RGBA_8888;
246
247 *pSurface = (gr_surface) surface;
248
249 exit:
thatc26ba0d2015-02-13 01:09:55 +0100250 fclose(fp);
Ethan Yonker448c8dc2014-12-08 15:34:34 -0600251 png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
252 if (result < 0 && surface != NULL) free(surface);
Dees_Troy51a0e822012-09-05 15:24:24 -0400253 return result;
254}
255
Ethan Yonkere95c4862016-01-27 10:57:26 -0600256#ifdef TW_INCLUDE_JPEG
Dees_Troy51a0e822012-09-05 15:24:24 -0400257int res_create_surface_jpg(const char* name, gr_surface* pSurface) {
258 GGLSurface* surface = NULL;
Ethan Yonkerfbb43532015-12-28 21:54:50 +0100259 int result = 0, y;
Dees_Troy51a0e822012-09-05 15:24:24 -0400260 struct jpeg_decompress_struct cinfo;
261 struct jpeg_error_mgr jerr;
Ethan Yonkerfbb43532015-12-28 21:54:50 +0100262 unsigned char* pData;
263 size_t width, height, stride, pixelSize;
Dees_Troy51a0e822012-09-05 15:24:24 -0400264
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
Ethan Yonkerfbb43532015-12-28 21:54:50 +0100291 width = cinfo.image_width;
292 height = cinfo.image_height;
293 stride = 4 * width;
294 pixelSize = stride * height;
Dees_Troy51a0e822012-09-05 15:24:24 -0400295
Ethan Yonkerfbb43532015-12-28 21:54:50 +0100296 surface = reinterpret_cast<GGLSurface*>(malloc(sizeof(GGLSurface) + pixelSize));
297 //p_row = reinterpret_cast<unsigned char*>(malloc(width * 4));
Dees_Troy51a0e822012-09-05 15:24:24 -0400298 if (surface == NULL) {
299 result = -8;
300 goto exit;
301 }
302
Ethan Yonkerfbb43532015-12-28 21:54:50 +0100303 pData = (unsigned char*) (surface + 1);
Dees_Troy51a0e822012-09-05 15:24:24 -0400304 surface->version = sizeof(GGLSurface);
305 surface->width = width;
306 surface->height = height;
307 surface->stride = width; /* Yes, pixels, not bytes */
308 surface->data = pData;
309 surface->format = GGL_PIXEL_FORMAT_RGBX_8888;
310
Dees_Troy51a0e822012-09-05 15:24:24 -0400311 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;
bigbiffe4a84df2022-04-10 14:47:39 -0400323#if defined(RECOVERY_ARGB) || defined(RECOVERY_BGRA)
Ethan Yonkerfbb43532015-12-28 21:54:50 +0100324 pRow[dx ] = b; // r
325 pRow[dx + 1] = g; // g
326 pRow[dx + 2] = r; // b
327 pRow[dx + 3] = a;
328#else
Dees_Troy51a0e822012-09-05 15:24:24 -0400329 pRow[dx ] = r; // r
330 pRow[dx + 1] = g; // g
331 pRow[dx + 2] = b; // b
332 pRow[dx + 3] = a;
Ethan Yonkerfbb43532015-12-28 21:54:50 +0100333#endif
Dees_Troy51a0e822012-09-05 15:24:24 -0400334 }
335 }
336 *pSurface = (gr_surface) surface;
337
338exit:
339 if (fp != NULL)
340 {
341 if (surface)
342 {
343 (void) jpeg_finish_decompress(&cinfo);
344 if (result < 0)
345 {
346 free(surface);
347 }
348 }
349 jpeg_destroy_decompress(&cinfo);
350 fclose(fp);
351 }
352 return result;
353}
Ethan Yonkere95c4862016-01-27 10:57:26 -0600354#endif
Dees_Troy51a0e822012-09-05 15:24:24 -0400355
356int res_create_surface(const char* name, gr_surface* pSurface) {
357 int ret;
Dees_Troy51a0e822012-09-05 15:24:24 -0400358 if (!name) return -1;
359
Ethan Yonkere95c4862016-01-27 10:57:26 -0600360#ifdef TW_INCLUDE_JPEG
Dees_Troy51a0e822012-09-05 15:24:24 -0400361 if (strlen(name) > 4 && strcmp(name + strlen(name) - 4, ".jpg") == 0)
Ethan Yonker63e414f2015-02-06 15:44:39 -0600362 return res_create_surface_jpg(name,pSurface);
Ethan Yonkere95c4862016-01-27 10:57:26 -0600363#endif
Dees_Troy51a0e822012-09-05 15:24:24 -0400364
bigbiffd81833a2021-01-17 11:06:57 -0500365 ret = res_create_surface_png(name, pSurface);
Ethan Yonkere95c4862016-01-27 10:57:26 -0600366#ifdef TW_INCLUDE_JPEG
Dees_Troy51a0e822012-09-05 15:24:24 -0400367 if (ret < 0)
368 ret = res_create_surface_jpg(name,pSurface);
Ethan Yonkere95c4862016-01-27 10:57:26 -0600369#endif
Dees_Troy51a0e822012-09-05 15:24:24 -0400370
371 return ret;
372}
373
374void res_free_surface(gr_surface surface) {
375 GGLSurface* pSurface = (GGLSurface*) surface;
376 if (pSurface) {
377 free(pSurface);
378 }
379}
Ethan Yonker63e414f2015-02-06 15:44:39 -0600380
381// Scale image function
382int res_scale_surface(gr_surface source, gr_surface* destination, float scale_w, float scale_h) {
Ethan Yonker727aeb82015-02-10 18:07:44 -0600383 GGLContext *gl = NULL;
Ethan Yonker63e414f2015-02-06 15:44:39 -0600384 GGLSurface* sc_mem_surface = NULL;
385 *destination = NULL;
386 GGLSurface *surface = (GGLSurface*)source;
387 int w = gr_get_width(source), h = gr_get_height(source);
388 int sx = 0, sy = 0, dx = 0, dy = 0;
389 float dw = (float)w * scale_w;
390 float dh = (float)h * scale_h;
391
392 // Create a new surface that is the appropriate size
393 sc_mem_surface = init_display_surface((int)dw, (int)dh);
394 if (!sc_mem_surface) {
395 printf("gr_scale_surface failed to init_display_surface\n");
396 return -1;
397 }
398 sc_mem_surface->format = surface->format;
399
Ethan Yonker727aeb82015-02-10 18:07:44 -0600400 // Initialize the context
401 gglInit(&gl);
Ethan Yonker63e414f2015-02-06 15:44:39 -0600402 gl->colorBuffer(gl, sc_mem_surface);
403 gl->activeTexture(gl, 0);
404
405 // Enable or disable blending based on source surface format
406 if (surface->format == GGL_PIXEL_FORMAT_RGBX_8888) {
407 gl->disable(gl, GGL_BLEND);
408 } else {
409 gl->enable(gl, GGL_BLEND);
410 gl->blendFunc(gl, GGL_ONE, GGL_ZERO);
411 }
412
413 // Bind our source surface to the context
414 gl->bindTexture(gl, surface);
415
416 // Deal with the scaling
417 gl->texParameteri(gl, GGL_TEXTURE_2D, GGL_TEXTURE_MIN_FILTER, GGL_LINEAR);
418 gl->texParameteri(gl, GGL_TEXTURE_2D, GGL_TEXTURE_MAG_FILTER, GGL_LINEAR);
419 gl->texParameteri(gl, GGL_TEXTURE_2D, GGL_TEXTURE_WRAP_S, GGL_CLAMP);
420 gl->texParameteri(gl, GGL_TEXTURE_2D, GGL_TEXTURE_WRAP_T, GGL_CLAMP);
421 gl->texEnvi(gl, GGL_TEXTURE_ENV, GGL_TEXTURE_ENV_MODE, GGL_REPLACE);
422 gl->texGeni(gl, GGL_S, GGL_TEXTURE_GEN_MODE, GGL_AUTOMATIC);
423 gl->texGeni(gl, GGL_T, GGL_TEXTURE_GEN_MODE, GGL_AUTOMATIC);
424 gl->enable(gl, GGL_TEXTURE_2D);
425
426 int32_t grad[8];
427 memset(grad, 0, sizeof(grad));
428 // s, dsdx, dsdy, scale, t, dtdx, dtdy, tscale <- this is wrong!
429 // This api uses block floating-point for S and T texture coordinates.
430 // All values are given in 16.16, scaled by 'scale'. In other words,
431 // set scale to 0, for 16.16 values.
432
433 // s, dsdx, dsdy, t, dtdx, dtdy, sscale, tscale
434 float dsdx = (float)w / dw;
435 float dtdy = (float)h / dh;
436 grad[0] = ((float)sx - (dsdx * dx)) * 65536;
437 grad[1] = dsdx * 65536;
438 grad[3] = ((float)sy - (dtdy * dy)) * 65536;
439 grad[5] = dtdy * 65536;
440// 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",
441// w, h, dx, dy, dw, dh, dsdx, dtdy, grad[0], grad[1], grad[3], grad[5]);
442 gl->texCoordGradScale8xv(gl, 0 /*tmu*/, grad);
443
444 // draw / scale the source surface to our target context
445 gl->recti(gl, dx, dy, dx + dw, dy + dh);
Ethan Yonker727aeb82015-02-10 18:07:44 -0600446 gglUninit(gl);
447 gl = NULL;
Ethan Yonker63e414f2015-02-06 15:44:39 -0600448 // put the scaled surface in our destination
449 *destination = (gr_surface*) sc_mem_surface;
450 // free memory used in the source
451 res_free_surface(source);
452 source = NULL;
453 return 0;
454}