Commit | Line | Data |
---|---|---|
3719602c PC |
1 | /* Copyright (C) 2010-2020 The RetroArch team |
2 | * | |
3 | * --------------------------------------------------------------------------------------- | |
4 | * The following license statement only applies to this file (rbmp_encode.c). | |
5 | * --------------------------------------------------------------------------------------- | |
6 | * | |
7 | * Permission is hereby granted, free of charge, | |
8 | * to any person obtaining a copy of this software and associated documentation files (the "Software"), | |
9 | * to deal in the Software without restriction, including without limitation the rights to | |
10 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, | |
11 | * and to permit persons to whom the Software is furnished to do so, subject to the following conditions: | |
12 | * | |
13 | * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. | |
14 | * | |
15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, | |
16 | * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | |
18 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, | |
19 | * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |
21 | */ | |
22 | ||
23 | #include <stdlib.h> | |
24 | #include <string.h> | |
25 | ||
26 | #include <streams/file_stream.h> | |
27 | #include <formats/rbmp.h> | |
28 | ||
29 | void form_bmp_header(uint8_t *header, | |
30 | unsigned width, unsigned height, | |
31 | bool is32bpp) | |
32 | { | |
33 | unsigned line_size = (width * (is32bpp?4:3) + 3) & ~3; | |
34 | unsigned size = line_size * height + 54; | |
35 | unsigned size_array = line_size * height; | |
36 | ||
37 | /* Generic BMP stuff. */ | |
38 | /* signature */ | |
39 | header[0] = 'B'; | |
40 | header[1] = 'M'; | |
41 | /* file size */ | |
42 | header[2] = (uint8_t)(size >> 0); | |
43 | header[3] = (uint8_t)(size >> 8); | |
44 | header[4] = (uint8_t)(size >> 16); | |
45 | header[5] = (uint8_t)(size >> 24); | |
46 | /* reserved */ | |
47 | header[6] = 0; | |
48 | header[7] = 0; | |
49 | header[8] = 0; | |
50 | header[9] = 0; | |
51 | /* offset */ | |
52 | header[10] = 54; | |
53 | header[11] = 0; | |
54 | header[12] = 0; | |
55 | header[13] = 0; | |
56 | /* DIB size */ | |
57 | header[14] = 40; | |
58 | header[15] = 0; | |
59 | header[16] = 0; | |
60 | header[17] = 0; | |
61 | /* Width */ | |
62 | header[18] = (uint8_t)(width >> 0); | |
63 | header[19] = (uint8_t)(width >> 8); | |
64 | header[20] = (uint8_t)(width >> 16); | |
65 | header[21] = (uint8_t)(width >> 24); | |
66 | /* Height */ | |
67 | header[22] = (uint8_t)(height >> 0); | |
68 | header[23] = (uint8_t)(height >> 8); | |
69 | header[24] = (uint8_t)(height >> 16); | |
70 | header[25] = (uint8_t)(height >> 24); | |
71 | /* Color planes */ | |
72 | header[26] = 1; | |
73 | header[27] = 0; | |
74 | /* Bits per pixel */ | |
75 | header[28] = is32bpp ? 32 : 24; | |
76 | header[29] = 0; | |
77 | /* Compression method */ | |
78 | header[30] = 0; | |
79 | header[31] = 0; | |
80 | header[32] = 0; | |
81 | header[33] = 0; | |
82 | /* Image data size */ | |
83 | header[34] = (uint8_t)(size_array >> 0); | |
84 | header[35] = (uint8_t)(size_array >> 8); | |
85 | header[36] = (uint8_t)(size_array >> 16); | |
86 | header[37] = (uint8_t)(size_array >> 24); | |
87 | /* Horizontal resolution */ | |
88 | header[38] = 19; | |
89 | header[39] = 11; | |
90 | header[40] = 0; | |
91 | header[41] = 0; | |
92 | /* Vertical resolution */ | |
93 | header[42] = 19; | |
94 | header[43] = 11; | |
95 | header[44] = 0; | |
96 | header[45] = 0; | |
97 | /* Palette size */ | |
98 | header[46] = 0; | |
99 | header[47] = 0; | |
100 | header[48] = 0; | |
101 | header[49] = 0; | |
102 | /* Important color count */ | |
103 | header[50] = 0; | |
104 | header[51] = 0; | |
105 | header[52] = 0; | |
106 | header[53] = 0; | |
107 | } | |
108 | ||
109 | static bool write_header_bmp(RFILE *file, unsigned width, unsigned height, bool is32bpp) | |
110 | { | |
111 | uint8_t header[54]; | |
112 | form_bmp_header(header, width, height, is32bpp); | |
113 | return filestream_write(file, header, sizeof(header)) == sizeof(header); | |
114 | } | |
115 | ||
116 | static void dump_line_565_to_24(uint8_t *line, const uint16_t *src, unsigned width) | |
117 | { | |
118 | unsigned i; | |
119 | ||
120 | for (i = 0; i < width; i++) | |
121 | { | |
122 | uint16_t pixel = *src++; | |
123 | uint8_t b = (pixel >> 0) & 0x1f; | |
124 | uint8_t g = (pixel >> 5) & 0x3f; | |
125 | uint8_t r = (pixel >> 11) & 0x1f; | |
126 | *line++ = (b << 3) | (b >> 2); | |
127 | *line++ = (g << 2) | (g >> 4); | |
128 | *line++ = (r << 3) | (r >> 2); | |
129 | } | |
130 | } | |
131 | ||
132 | static void dump_line_32_to_24(uint8_t *line, const uint32_t *src, unsigned width) | |
133 | { | |
134 | unsigned i; | |
135 | ||
136 | for (i = 0; i < width; i++) | |
137 | { | |
138 | uint32_t pixel = *src++; | |
139 | *line++ = (pixel >> 0) & 0xff; | |
140 | *line++ = (pixel >> 8) & 0xff; | |
141 | *line++ = (pixel >> 16) & 0xff; | |
142 | } | |
143 | } | |
144 | ||
145 | static void dump_content(RFILE *file, const void *frame, | |
146 | int width, int height, int pitch, enum rbmp_source_type type) | |
147 | { | |
148 | int j; | |
149 | size_t line_size; | |
150 | uint8_t *line = NULL; | |
151 | int bytes_per_pixel = (type==RBMP_SOURCE_TYPE_ARGB8888?4:3); | |
152 | union | |
153 | { | |
154 | const uint8_t *u8; | |
155 | const uint16_t *u16; | |
156 | const uint32_t *u32; | |
157 | } u; | |
158 | ||
159 | u.u8 = (const uint8_t*)frame; | |
160 | line_size = (width * bytes_per_pixel + 3) & ~3; | |
161 | ||
162 | switch (type) | |
163 | { | |
164 | case RBMP_SOURCE_TYPE_BGR24: | |
165 | { | |
166 | /* BGR24 byte order input matches output. Can directly copy, but... need to make sure we pad it. */ | |
167 | uint32_t zeros = 0; | |
168 | int padding = (int)(line_size-pitch); | |
169 | for (j = 0; j < height; j++, u.u8 += pitch) | |
170 | { | |
171 | filestream_write(file, u.u8, pitch); | |
172 | if (padding != 0) | |
173 | filestream_write(file, &zeros, padding); | |
174 | } | |
175 | } | |
176 | break; | |
177 | case RBMP_SOURCE_TYPE_ARGB8888: | |
178 | /* ARGB8888 byte order input matches output. Can directly copy. */ | |
179 | for (j = 0; j < height; j++, u.u8 += pitch) | |
180 | filestream_write(file, u.u8, line_size); | |
181 | return; | |
182 | default: | |
183 | break; | |
184 | } | |
185 | ||
186 | /* allocate line buffer, and initialize the final four bytes to zero, for deterministic padding */ | |
187 | if (!(line = (uint8_t*)malloc(line_size))) | |
188 | return; | |
189 | *(uint32_t*)(line + line_size - 4) = 0; | |
190 | ||
191 | switch (type) | |
192 | { | |
193 | case RBMP_SOURCE_TYPE_XRGB888: | |
194 | for (j = 0; j < height; j++, u.u8 += pitch) | |
195 | { | |
196 | dump_line_32_to_24(line, u.u32, width); | |
197 | filestream_write(file, line, line_size); | |
198 | } | |
199 | break; | |
200 | case RBMP_SOURCE_TYPE_RGB565: | |
201 | for (j = 0; j < height; j++, u.u8 += pitch) | |
202 | { | |
203 | dump_line_565_to_24(line, u.u16, width); | |
204 | filestream_write(file, line, line_size); | |
205 | } | |
206 | break; | |
207 | default: | |
208 | break; | |
209 | } | |
210 | ||
211 | /* Free allocated line buffer */ | |
212 | free(line); | |
213 | } | |
214 | ||
215 | bool rbmp_save_image( | |
216 | const char *filename, | |
217 | const void *frame, | |
218 | unsigned width, unsigned height, | |
219 | unsigned pitch, enum rbmp_source_type type) | |
220 | { | |
221 | bool ret = false; | |
222 | RFILE *file = filestream_open(filename, | |
223 | RETRO_VFS_FILE_ACCESS_WRITE, | |
224 | RETRO_VFS_FILE_ACCESS_HINT_NONE); | |
225 | if (!file) | |
226 | return false; | |
227 | ||
228 | ret = write_header_bmp(file, width, height, type==RBMP_SOURCE_TYPE_ARGB8888); | |
229 | ||
230 | if (ret) | |
231 | dump_content(file, frame, width, height, pitch, type); | |
232 | ||
233 | filestream_close(file); | |
234 | ||
235 | return ret; | |
236 | } |