6d7ab8aac41ffde2eebd25e8310dec0136510654
[metawatch.git] / mw_utility.c
1 /*
2  * (c) 2011 Siegen, Germany by Nils Faerber <nils.faerber@kernelconcepts.de>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19
20 #include <stdlib.h>
21 #include <string.h>
22 #include <stdio.h>
23
24 #include "metawatch.h"
25 #include "mw_utility.h"
26
27 #include "fonts.h"
28
29 /* ----------------------------------------------------------------------
30  * Generic drawing functions
31  * ---------------------------------------------------------------------- */
32
33 /*
34  * The pixmap buffer has at least one byte per pixel, even for monochrome (bpp=1)
35  * bitmaps
36  */
37 mw_buffer *mw_alloc_pbuffer(unsigned int res_x, unsigned int res_y, unsigned int bpp)
38 {
39         mw_buffer *nmwbuf;
40         int pbuf_size;
41
42         nmwbuf = (mw_buffer *) malloc(sizeof(mw_buffer));
43         if (!nmwbuf)
44                 return NULL;
45
46         nmwbuf->res_x = res_x;
47         nmwbuf->res_y = res_y;
48         nmwbuf->bpp = bpp;
49
50         pbuf_size = nmwbuf->res_x * nmwbuf->res_y * ((nmwbuf->bpp / 8) + 1);
51         nmwbuf->pbuf = malloc(pbuf_size);
52         if (!nmwbuf->pbuf) {
53                 free(nmwbuf);
54                 return NULL;
55         } else {
56                 memset(nmwbuf->pbuf, 0, pbuf_size);
57                 return nmwbuf;
58         }
59 }
60
61 void mw_free_pbuffer(mw_buffer *mwbuf)
62 {
63         if (!mwbuf)
64                 return;
65
66         free(mwbuf->pbuf);
67         free(mwbuf);
68 }
69
70 /*
71  * makes a buffer for sending to the LCD watch from the drawing buffer, e.g.
72  * mw_send_bitmap(mw_fd, MW_SCREEN_MODE_IDLE, 96, 65, 31, bbuf, len);
73  *
74  * NOT reentrant !
75  */
76 unsigned char *mw_make_mw_buffer(mw_buffer *mwbuf, int *buflen)
77 {
78         static unsigned char wbuf[96*12];
79         int x, y;
80         unsigned char clr;
81
82         memset(wbuf, 0, 96*12);
83
84         for (y = 0; y < mwbuf->res_y; y++) {
85                 for (x = 0; x < mwbuf->res_x; x++) {
86                         clr = *(unsigned char *)(mwbuf->pbuf+((y*mwbuf->res_x)+x));
87                         if (clr) {
88                                 *(unsigned char *)(wbuf+((y*12)+(x/8))) |= 1 << (x%8);
89                         };
90                 };
91         };
92         *buflen = mwbuf->res_y * 12;
93
94         return wbuf;
95 }
96
97 unsigned char *mw_make_mw_oled_buffer(mw_buffer *mwbuf, int *buflen)
98 {
99         static unsigned char wbuf[2*80]; /* size of one OLED, two rows */
100         int x, y;
101         unsigned char clr;
102
103         memset(wbuf, 0, 2*80);
104
105         for (x=0; x<mwbuf->res_x; x++) {
106                 for (y=0; y<mwbuf->res_y; y++) {
107                         clr = *(unsigned char *)(mwbuf->pbuf+((y*mwbuf->res_x)+x));
108                         if (clr) {
109                                 *(unsigned char *)(wbuf+(x+80*(y/8))) |= 1 << (7-(y%8));
110                         }
111                 }
112         }
113         *buflen = (mwbuf->res_y / 8) * 80;
114
115         return wbuf;
116 }
117
118 void mw_dump_mw_buffer(mw_buffer *mwbuf)
119 {
120         int x, y;
121         unsigned char clr;
122
123         for (y = 0; y < mwbuf->res_y; y++) {
124                 for (x = 0; x < mwbuf->res_x; x++) {
125                         clr = *(unsigned char *)(mwbuf->pbuf+((y*mwbuf->res_x)+x));
126                         if (clr)
127                                 fprintf(stderr, ".");
128                         else
129                                 fprintf(stderr, " ");
130                 };
131                 fprintf(stderr, "\n");
132         };
133 }
134
135
136 /* clear/fill entire buffer with color */
137 void mw_buf_clear(mw_buffer *mwbuf, mw_color clr)
138 {
139         int pbuf_size;
140
141         if (!mwbuf)
142                 return;
143         if (clr == MW_TRANSPARENT)
144                 return;
145
146         pbuf_size = mwbuf->res_x * mwbuf->res_y * ((mwbuf->bpp / 8) + 1);
147         memset(mwbuf->pbuf, clr, pbuf_size);
148 }
149
150 /* draw a single pixel */
151 void mw_buf_draw_pixel(mw_buffer *mwbuf, unsigned int x, unsigned int y, mw_color clr)
152 {
153         if (!mwbuf)
154                 return;
155         if (clr == MW_TRANSPARENT)
156                 return;
157
158         *(unsigned char *)(mwbuf->pbuf+((y*mwbuf->res_x)+x)) = clr;
159 }
160
161 void mw_buf_print(mw_buffer *mwbuf, unsigned int x, unsigned int y, char *text, unsigned char fsize, mw_color fgclr, mw_color bgclr)
162 {
163         unsigned int  i,j,z;
164         const unsigned char *data, *font_style;
165         unsigned char mask,xme,yme,offset;
166
167         if (text==NULL || strlen(text) == 0)
168                 return;
169
170         switch (fsize) {
171                 case 0:
172                         data = (const unsigned char *)FONT6x8;
173                         font_style = (const unsigned char *)FONT6x8;
174                         break;
175                 case 1:
176                         data = (const unsigned char *)FONT8x8F;
177                         font_style = (const unsigned char *)FONT8x8F;
178                         break;
179                 case 2:
180                         data = (const unsigned char *)FONT8x16;
181                         font_style = (const unsigned char *)FONT8x16;
182                         break;
183                 default:
184                         data = (const unsigned char *)FONT6x8;
185                         font_style = (const unsigned char *)FONT6x8;
186                         break;
187         };
188         xme = *data++;
189         yme = *data++;
190         offset = *data;
191
192         do {
193                 mask = 0x00;
194                 data =  (font_style + offset) + (offset * (int)(*text - 32));
195                 for (i=0; i < yme; i++) {
196                         mask |=0x80;
197                         for (j=x; j < (x + xme); j++) {
198                                 z = y + i;
199                                 if ((z < mwbuf->res_y) && (j < mwbuf->res_x)) {
200                                         if (*data & mask) {
201                                                 mw_buf_draw_pixel(mwbuf, j, z, fgclr);
202                                         } else {
203                                                 mw_buf_draw_pixel(mwbuf, j, z, bgclr);
204                                         }
205                                 }
206                                 mask >>= 1;
207                         }
208                         data++;
209                 }
210                 x += xme;
211                 text++;
212         } while (*text != '\0');
213 }
214
215 void mw_buf_draw_line_bresenham(mw_buffer *mwbuf, unsigned int xstart, unsigned int ystart, unsigned int xend, unsigned int yend, mw_color clr)
216 {
217         int x, y, t, dx, dy, incx, incy, pdx, pdy, ddx, ddy, es, el, err;
218  
219         dx = xend - xstart;
220         dy = yend - ystart;
221  
222         incx = (dx >= 0) ? 1 : -1;
223         incy = (dy >= 0) ? 1 : -1;
224
225         if (dx<0)
226                 dx = -dx;
227         if (dy<0)
228                 dy = -dy;
229  
230         if (dx>dy) {
231                 pdx = incx; pdy = 0;
232                 ddx=incx; ddy=incy;
233                 es =dy;   el =dx;
234         } else {
235                 pdx=0;    pdy=incy;
236                 ddx=incx; ddy=incy;
237                 es =dx;   el =dy;
238         }
239  
240         x = xstart;
241         y = ystart;
242         err = el/2;
243         mw_buf_draw_pixel(mwbuf, x, y, clr);
244  
245         for (t = 0; t < el; ++t) {
246                 err -= es; 
247                 if (err < 0) {
248                         err += el;
249                         x += ddx;
250                         y += ddy;
251                 } else {
252                         x += pdx;
253                         y += pdy;
254                 }
255                 mw_buf_draw_pixel(mwbuf, x, y, clr);
256         }
257 }
258
259 void mw_buf_draw_line_bresenham_w(mw_buffer *mwbuf, unsigned int xstart, unsigned int ystart, unsigned int xend, unsigned int yend, unsigned char thickness, mw_color clr)
260 {
261         int i, x, y, t, dx, dy, incx, incy, pdx, pdy, ddx, ddy, es, el, err;
262  
263         dx = xend - xstart;
264         dy = yend - ystart;
265  
266         incx = (dx >= 0) ? 1 : -1;
267         incy = (dy >= 0) ? 1 : -1;
268
269         if (dx<0)
270                 dx = -dx;
271         if (dy<0)
272                 dy = -dy;
273  
274         if (dx>dy) {
275                 pdx = incx; pdy = 0;
276                 ddx=incx; ddy=incy;
277                 es =dy;   el =dx;
278         } else {
279                 pdx=0;    pdy=incy;
280                 ddx=incx; ddy=incy;
281                 es =dx;   el =dy;
282         }
283  
284         x = xstart;
285         y = ystart;
286         err = el/2;
287         mw_buf_draw_pixel(mwbuf, x, y, clr);
288         for (i=1; i<thickness; i++) {
289                 mw_buf_draw_pixel(mwbuf, x-i, y, clr);
290                 mw_buf_draw_pixel(mwbuf, x+i, y, clr);
291                 mw_buf_draw_pixel(mwbuf, x, y-i, clr);
292                 mw_buf_draw_pixel(mwbuf, x, y+i, clr);
293         }
294  
295         for (t = 0; t < el; ++t) {
296                 err -= es; 
297                 if (err < 0) {
298                         err += el;
299                         x += ddx;
300                         y += ddy;
301                 } else {
302                         x += pdx;
303                         y += pdy;
304                 }
305                 mw_buf_draw_pixel(mwbuf, x, y, clr);
306                 for (i=1; i<thickness; i++) {
307                         mw_buf_draw_pixel(mwbuf, x-i, y, clr);
308                         mw_buf_draw_pixel(mwbuf, x+i, y, clr);
309                         mw_buf_draw_pixel(mwbuf, x, y-i, clr);
310                         mw_buf_draw_pixel(mwbuf, x, y+i, clr);
311                 }
312         }
313 }
314
315 /* ----------------------------------------------------------------------
316  * Complex combined functions, for user convenience
317  * ---------------------------------------------------------------------- */
318
319 /*
320  * send a text notification, automatically take care of device type (ana/digi)
321  * char *title is displayed inverse in top line
322  * char *text is the notification text
323  * vibrate is the number of 300ms vibrations, 0 for none
324  */
325 void mw_do_notification(mwdevice_t *mwdevice, char *title, char *text, unsigned char vibrate)
326 {
327         mw_buffer *mwbuf;
328         unsigned char *bbuf;
329         int len,i,c,r;
330         char sstr[32];
331
332         // fprintf(stderr, "do_notify devtype=%d, title='%s', text='%s', vibrate=%d\n", mwdevice->devtype, title, text, vibrate);
333         if (mwdevice->devtype == MW_DEVICE_TYPE_DIGITAL || mwdevice->devtype == MW_DEVICE_TYPE_DEVB_DIGI) {
334                 mwbuf = mw_alloc_pbuffer(96, 96, 1);
335                 mw_buf_clear(mwbuf, MW_BLACK);
336
337                 mw_buf_print(mwbuf, 0,  0, title, 0, MW_BLACK, MW_WHITE);
338
339                 i=0;
340                 c=0; r=1;
341                 memset(sstr,0,32);
342                 while (i<strlen(text)) {
343                         sstr[c++] = text[i++];
344                         if (c>=16 || i>=strlen(text)) {
345                                 mw_buf_print(mwbuf, 0,  r*9, sstr, 0, MW_WHITE, MW_BLACK);
346                                 memset(sstr,0,32);
347                                 c=0; r++;
348                                 if (r>10)
349                                         break;
350                         };
351                 };
352
353                 bbuf = mw_make_mw_buffer(mwbuf, &len);
354                 mw_send_bitmap(mwdevice, MW_SCREEN_MODE_NOTIFICATION, 96, 96, 0, bbuf, len);
355                 mw_update_display(mwdevice, MW_SCREEN_MODE_NOTIFICATION, 1);
356                 mw_free_pbuffer(mwbuf);
357         } else if (mwdevice->devtype == MW_DEVICE_TYPE_ANA_DIGI || mwdevice->devtype == MW_DEVICE_TYPE_DEVB_ANA_DIGI) {
358                 fprintf(stderr, "do notify OLED\n");
359                 mwbuf = mw_alloc_pbuffer(80, 16, 1);
360                 mw_buf_clear(mwbuf, MW_BLACK);
361
362                 mw_buf_print(mwbuf, 0,  0, title, 0, MW_BLACK, MW_WHITE);
363
364                 i=0;
365                 c=0; r=1;
366                 memset(sstr,0,32);
367                 while (i<strlen(text) && r<2) {
368                         sstr[c++] = text[i++];
369                         if (c>=13 || i>=strlen(text)) {
370                                 mw_buf_print(mwbuf, 0,  r*9, sstr, 0, MW_WHITE, MW_BLACK);
371                                 memset(sstr,0,32);
372                                 c=0; r++;
373                         };
374                 };
375
376                 bbuf = mw_make_mw_oled_buffer(mwbuf, &len);
377                 mw_write_oled_buffer(mwdevice, 0, MW_OLED_UPPER, 80, 0, bbuf, len);
378
379                 mw_buf_clear(mwbuf, MW_BLACK);
380                 c=0; r=0;
381                 memset(sstr,0,32);
382                 while (i<strlen(text) && r<2) {
383                         sstr[c++] = text[i++];
384                         if (c>=13 || i>=strlen(text)) {
385                                 mw_buf_print(mwbuf, 0,  r*9, sstr, 0, MW_WHITE, MW_BLACK);
386                                 memset(sstr,0,32);
387                                 c=0; r++;
388                                 if (r>2)
389                                         break;
390                         };
391                 };
392
393                 bbuf = mw_make_mw_oled_buffer(mwbuf, &len);
394                 mw_write_oled_buffer(mwdevice, 0, MW_OLED_LOWER, 80, 0, bbuf, len);
395
396                 mw_free_pbuffer(mwbuf);
397         } else
398                 fprintf(stderr, "Watch type not set - forgot to call mw_init()?\n");
399
400         if (vibrate)
401                 mw_set_vibrate_mode(mwdevice, 1, 300, 300, vibrate);
402 }
403