blob: 0bc86828d1e951d96c7a0a8b60ec7d69eb9b2a6c [file] [log] [blame]
Vojtech Bocek03fd6c52014-03-13 18:46:34 +01001/*
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 <stdio.h>
Matt Mower2b18a532015-02-20 16:58:05 -060018#include <stdlib.h>
Vojtech Bocek03fd6c52014-03-13 18:46:34 +010019#include <png.h>
20#include <pixelflinger/pixelflinger.h>
21#include <linux/fb.h>
Vladimir Olteand32b7eb2018-07-03 00:04:03 +030022#include <string.h>
Vojtech Bocek03fd6c52014-03-13 18:46:34 +010023
24#include "minui.h"
25
26struct fb_var_screeninfo vi;
Ethan Yonkerfbb43532015-12-28 21:54:50 +010027extern GGLSurface gr_mem_surface;
28extern GRSurface* gr_draw;
Aaron Kling4eccd9a2019-06-30 12:20:09 -050029extern unsigned int gr_rotation;
Vojtech Bocek03fd6c52014-03-13 18:46:34 +010030
31int gr_save_screenshot(const char *dest)
32{
33 uint32_t y, stride_bytes;
xiaolu26ffa862015-05-23 12:11:09 +080034 volatile int res = -1;
Vojtech Bocek03fd6c52014-03-13 18:46:34 +010035 GGLContext *gl = NULL;
36 GGLSurface surface;
37 uint8_t * volatile img_data = NULL;
38 uint8_t *ptr;
xiaolu26ffa862015-05-23 12:11:09 +080039 FILE * volatile fp = NULL;
Vojtech Bocek03fd6c52014-03-13 18:46:34 +010040 png_structp png_ptr = NULL;
41 png_infop info_ptr = NULL;
42
43 fp = fopen(dest, "wb");
44 if(!fp)
45 goto exit;
46
Ethan Yonkerfbb43532015-12-28 21:54:50 +010047 img_data = (uint8_t *)malloc(gr_mem_surface.stride * gr_mem_surface.height * gr_draw->pixel_bytes);
48 if (!img_data) {
49 printf("gr_save_screenshot failed to malloc img_data\n");
50 goto exit;
51 }
Vojtech Bocek03fd6c52014-03-13 18:46:34 +010052 surface.version = sizeof(surface);
53 surface.width = gr_mem_surface.width;
54 surface.height = gr_mem_surface.height;
xiaolu26ffa862015-05-23 12:11:09 +080055 surface.stride = gr_mem_surface.stride;
Vojtech Bocek03fd6c52014-03-13 18:46:34 +010056 surface.data = img_data;
notsyncing8e4e8ec2018-06-09 10:40:50 +080057
58#if defined(RECOVERY_BGRA)
59 surface.format = GGL_PIXEL_FORMAT_BGRA_8888;
60#else
xiaolu26ffa862015-05-23 12:11:09 +080061 surface.format = GGL_PIXEL_FORMAT_RGBA_8888;
notsyncing8e4e8ec2018-06-09 10:40:50 +080062#endif
Vojtech Bocek03fd6c52014-03-13 18:46:34 +010063
64 gglInit(&gl);
65 gl->colorBuffer(gl, &surface);
66 gl->activeTexture(gl, 0);
67
xiaolu26ffa862015-05-23 12:11:09 +080068 if(gr_mem_surface.format == GGL_PIXEL_FORMAT_RGBX_8888)
69 gl->disable(gl, GGL_BLEND);
70
Vojtech Bocek03fd6c52014-03-13 18:46:34 +010071 gl->bindTexture(gl, &gr_mem_surface);
72 gl->texEnvi(gl, GGL_TEXTURE_ENV, GGL_TEXTURE_ENV_MODE, GGL_REPLACE);
73 gl->texGeni(gl, GGL_S, GGL_TEXTURE_GEN_MODE, GGL_ONE_TO_ONE);
74 gl->texGeni(gl, GGL_T, GGL_TEXTURE_GEN_MODE, GGL_ONE_TO_ONE);
75 gl->enable(gl, GGL_TEXTURE_2D);
76 gl->texCoord2i(gl, 0, 0);
77 gl->recti(gl, 0, 0, gr_mem_surface.width, gr_mem_surface.height);
78
79 gglUninit(gl);
80 gl = NULL;
81
82 png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
83 if (!png_ptr)
84 goto exit;
85
86 info_ptr = png_create_info_struct(png_ptr);
87 if (info_ptr == NULL)
88 goto exit;
89
90 if (setjmp(png_jmpbuf(png_ptr)))
91 goto exit;
92
93 png_init_io(png_ptr, fp);
94 png_set_IHDR(png_ptr, info_ptr, surface.width, surface.height,
95 8, PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE,
96 PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
97 png_write_info(png_ptr, info_ptr);
98
xiaolu26ffa862015-05-23 12:11:09 +080099 // To remove the alpha channel for PNG_COLOR_TYPE_RGB format,
100 png_set_filler(png_ptr, 0, PNG_FILLER_AFTER);
101
Vojtech Bocek03fd6c52014-03-13 18:46:34 +0100102 ptr = img_data;
xiaolu26ffa862015-05-23 12:11:09 +0800103 stride_bytes = surface.stride*4;
Vojtech Bocek03fd6c52014-03-13 18:46:34 +0100104 for(y = 0; y < surface.height; ++y)
105 {
106 png_write_row(png_ptr, ptr);
107 ptr += stride_bytes;
108 }
109
110 png_write_end(png_ptr, NULL);
111
112 res = 0;
113exit:
114 if(info_ptr)
115 png_free_data(png_ptr, info_ptr, PNG_FREE_ALL, -1);
116 if(png_ptr)
117 png_destroy_write_struct(&png_ptr, (png_infopp)NULL);
118 if(gl)
119 gglUninit(gl);
120 if(img_data)
121 free(img_data);
122 if(fp)
123 fclose(fp);
124 return res;
125}
Vladimir Olteand32b7eb2018-07-03 00:04:03 +0300126
Aaron Kling4eccd9a2019-06-30 12:20:09 -0500127int ROTATION_X_DISP(int x, int y, int w) {
128 return ((gr_rotation == 0) ? (x) :
129 (gr_rotation == 90) ? (w - (y) - 1) :
130 (gr_rotation == 180) ? (w - (x) - 1) :
131 (gr_rotation == 270) ? (y) : -1);
132}
133
134int ROTATION_Y_DISP(int x, int y, int h) {
135 return ((gr_rotation == 0) ? (y) :
136 (gr_rotation == 90) ? (x) :
137 (gr_rotation == 180) ? (h - (y) - 1) :
138 (gr_rotation == 270) ? (h - (x) - 1) : -1);
139}
140
Vladimir Olteand32b7eb2018-07-03 00:04:03 +0300141#define MATRIX_ELEMENT(matrix, row, col, row_size, elem_size) \
142 (((uint8_t*) (matrix)) + (((row) * (elem_size)) * (row_size)) + ((col) * (elem_size)))
143
144#define DO_MATRIX_ROTATION(bits_per_pixel, bytes_per_pixel) \
145{ \
146 for (size_t y = 0; y < src->height; y++) { \
147 for (size_t x = 0; x < src->width; x++) { \
148 /* output pointer in dst->data */ \
149 uint##bits_per_pixel##_t *op; \
150 /* input pointer from src->data */ \
151 const uint##bits_per_pixel##_t *ip; \
152 /* Display coordinates (in dst) corresponding to (x, y) in src */ \
Aaron Kling4eccd9a2019-06-30 12:20:09 -0500153 size_t x_disp = ROTATION_X_DISP(x, y, dst->width); \
154 size_t y_disp = ROTATION_Y_DISP(x, y, dst->height); \
Vladimir Olteand32b7eb2018-07-03 00:04:03 +0300155 \
156 ip = (const uint##bits_per_pixel##_t*) \
157 MATRIX_ELEMENT(src->data, y, x, \
158 src->stride, bytes_per_pixel); \
159 op = (uint##bits_per_pixel##_t*) \
160 MATRIX_ELEMENT(dst->data, y_disp, x_disp, \
161 dst->stride, bytes_per_pixel); \
162 *op = *ip; \
163 } \
164 } \
165}
166
167void surface_ROTATION_transform(gr_surface dst_ptr, const gr_surface src_ptr,
168 size_t num_bytes_per_pixel)
169{
170 GGLSurface *dst = (GGLSurface*) dst_ptr;
171 const GGLSurface *src = (GGLSurface*) src_ptr;
172
173 /* Handle duplicated code via a macro.
174 * This is currently used for rotating surfaces of graphical resources
175 * (32-bit pixel format) and of font glyphs (8-bit pixel format).
176 * If you need to add handling of other pixel formats feel free to do so.
177 */
178 if (num_bytes_per_pixel == 4) {
179 DO_MATRIX_ROTATION(32, 4);
180 } else if (num_bytes_per_pixel == 1) {
181 DO_MATRIX_ROTATION(8, 1);
182 }
183}