0cf619d3113b29b2ff23b540e0e33dccc185dbd2
[pcsx_rearmed.git] / deps / libretro-common / formats / bmp / rbmp_encode.c
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 }