]> git.vomp.tv Git - vompclient.git/blob - surfacemvp.cc
Fix text corruption in channel number display on live tv
[vompclient.git] / surfacemvp.cc
1 /*
2     Copyright 2004-2005 Chris Tallon
3     Portions copyright 2004 Jon Gettler
4
5     This file is part of VOMP.
6
7     VOMP is free software; you can redistribute it and/or modify
8     it under the terms of the GNU General Public License as published by
9     the Free Software Foundation; either version 2 of the License, or
10     (at your option) any later version.
11
12     VOMP is distributed in the hope that it will be useful,
13     but WITHOUT ANY WARRANTY; without even the implied warranty of
14     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15     GNU General Public License for more details.
16
17     You should have received a copy of the GNU General Public License
18     along with VOMP; if not, write to the Free Software
19     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
20 */
21
22 #include "surfacemvp.h"
23
24 #include "osd.h"
25 #include "bitmap.h"
26 #include "log.h"
27
28 SurfaceMVP::SurfaceMVP(int id)
29 : Surface(id)
30 {
31   fdOsd = Osd::getInstance()->getFD();
32   memset(&surface, 0, sizeof(osd_surface_t));
33   memset(&surface.sfc, 0, sizeof(stbgfx_sfc_t));
34   memset(&surface.map, 0, sizeof(stbgfx_map_t));
35 }
36
37 SurfaceMVP::~SurfaceMVP()
38 {
39   for (int i = 0; i < 3; i++)
40   {
41     if (surface.base[i]) munmap(surface.base[i], surface.map.map[i].size);
42   }
43
44   int a;
45
46   a = ioctl(fdOsd, GFX_FB_SFC_UNMAP, surface.sfc.handle);
47   if (a) Log::getInstance()->log("Surface", Log::ERR, "Surface unmap failed");
48
49   a = ioctl(fdOsd, GFX_FB_SFC_FREE, surface.sfc.handle);
50   if (a) Log::getInstance()->log("Surface", Log::ERR, "Surface destroy failed");
51
52 }
53
54 int SurfaceMVP::create(UINT width, UINT height)
55 {
56   int r = 0;
57   int displayNumber = 0; // mvpmc iterates 0->15 till it gets one that works?
58
59   surface.sfc.width = width;
60   surface.sfc.height = height;
61   surface.sfc.flags = 0x3f1533;
62
63   surface.sfc.unknown = -1;
64
65 //  r = ioctl(fdOsd, GFX_FB_SET_OSD, &displayNumber);
66 //  if (r) return 0;
67
68   r = ioctl(fdOsd, GFX_FB_OSD_SURFACE, &displayNumber);
69   if (r) return 0;
70
71   r = ioctl(fdOsd, GFX_FB_SFC_ALLOC, &surface.sfc);
72   if (r) return 0;
73
74   surface.map.map[0].unknown = surface.sfc.handle;
75   r = ioctl(fdOsd, GFX_FB_MAP, &surface.map);
76   if (r) return 0;
77
78   surface.base[0] = (unsigned char *)mmap(NULL, surface.map.map[0].size, PROT_READ|PROT_WRITE, MAP_SHARED, fdOsd, surface.map.map[0].addr);
79   if (surface.base[0] == MAP_FAILED) return 0;
80
81   surface.base[1] = (unsigned char *)mmap(NULL, surface.map.map[1].size, PROT_READ|PROT_WRITE, MAP_SHARED, fdOsd, surface.map.map[1].addr);
82   if (surface.base[1] == MAP_FAILED) return 0;
83
84   surface.base[2] = (unsigned char *)mmap(NULL, surface.map.map[2].size, PROT_READ|PROT_WRITE, MAP_SHARED, fdOsd, surface.map.map[2].addr);
85   if (surface.base[2] == MAP_FAILED) return 0;
86
87 //  surface.display.num = displayNumber;
88 //  r = ioctl(fdOsd, GFX_FB_MOVE_DISPLAY, &surface.display);
89 //  if (r) return 0;
90
91 //  r = ioctl(fdOsd, GFX_FB_SET_DISPLAY, &surface.display);
92 //  if (r) return 0;
93
94   return 1;
95 }
96
97 void SurfaceMVP::display()
98 {
99   unsigned long fb_descriptor[2];
100
101   fb_descriptor[0] = surface.sfc.handle;
102   fb_descriptor[1] = 1;
103
104   ioctl(fdOsd, GFX_FB_ATTACH, fb_descriptor);
105 }
106
107 unsigned long SurfaceMVP::getSurfaceHandle()
108 {
109   return surface.sfc.handle;
110 }
111
112 // ----------------------------------------------------------------------------
113
114 // Now for the drawing functions
115
116 /*
117  * RGB to YUV conversion tables
118  */
119 int SurfaceMVP::conv_YB[256];
120 int SurfaceMVP::conv_YG[256];
121 int SurfaceMVP::conv_YR[256];
122 int SurfaceMVP::conv_UB[256];
123 int SurfaceMVP::conv_UG[256];
124 int SurfaceMVP::conv_UR[256];
125 int SurfaceMVP::conv_VB[256];
126 int SurfaceMVP::conv_VG[256];
127 int SurfaceMVP::conv_VR[256];
128
129 int SurfaceMVP::conv_BY[256];
130 int SurfaceMVP::conv_GY[256];
131 int SurfaceMVP::conv_RY[256];
132 int SurfaceMVP::conv_BU[256];
133 int SurfaceMVP::conv_GU[256];
134 int SurfaceMVP::conv_RU[256];
135 int SurfaceMVP::conv_BV[256];
136 int SurfaceMVP::conv_GV[256];
137 int SurfaceMVP::conv_RV[256];
138
139 /*
140  * gfx_init() - initialize the RGB to YUV conversion tables
141  */
142 void SurfaceMVP::initConversionTables(int rfactor, int gfactor, int bfactor)
143  {
144    int i;
145   double drfactor=(double)rfactor/(double)100;
146   double dgfactor=(double)gfactor/(double)100;
147   double dbfactor=(double)bfactor/(double)100;
148
149   for (i=0; i<256; i++) {
150     conv_YB[i] = (int)(0.299 * (double)i *dbfactor);
151     conv_BY[i] = i;
152   }
153   for (i=0; i<256; i++) {
154     conv_YG[i] = (int)(0.587 * (double)i * dgfactor);
155     conv_GY[i] = i;
156   }
157   for (i=0; i<256; i++) {
158     conv_YR[i] = (int)(0.114 * (double)i *drfactor);
159     conv_RY[i] = i;
160   }
161
162   for (i=0; i<256; i++) {
163     conv_UB[i] = (int)(0.5 * (double)i *dbfactor);
164     conv_BU[i] = (int)(1.732 * (i - 128));
165   }
166   for (i=0; i<256; i++) {
167     conv_UG[i] = (int)(-0.33126 * (double)i*dgfactor);
168     conv_GU[i] = (int)(-0.338 * (i - 128));
169   }
170   for (i=0; i<256; i++) {
171     conv_UR[i] = (int)(-0.16874 * (double)i *drfactor);
172     conv_RU[i] = 0;
173   }
174
175   for (i=0; i<256; i++) {
176     conv_VB[i] = (int)(-0.08131 * (double)i *dbfactor);
177     conv_BV[i] = 0;
178   }
179   for (i=0; i<256; i++) {
180     conv_VG[i] = (int)(-0.41869 * (double)i * dgfactor);
181     conv_GV[i] = (int)(-0.698 * (i - 128));
182   }
183   for (i=0; i<256; i++) {
184     conv_VR[i] = (int)(0.5 * (double)i * drfactor);
185     conv_RV[i] = (int)(1.370 * ((double)i - 128));
186   }
187 }
188
189 int SurfaceMVP::fillblt(int x, int y, int width, int height, const DrawStyle& c)
190 {
191   osd_fillblt_t fblt;
192
193   fblt.handle = surface.sfc.handle;
194   fblt.x = x;
195   fblt.y = y;
196   fblt.width = width;
197   fblt.height = height;
198   fblt.colour = c.rgba();
199
200   return ioctl(fdOsd, GFX_FB_OSD_FILLBLT, &fblt);
201 }
202
203 void SurfaceMVP::drawPixel(int x, int y, unsigned int c, bool fastdraw)
204 {
205   int offset;
206   unsigned char r, g, b, a, Y, U, V;
207   unsigned int line, remainder;
208
209   if ((x >= (int)surface.sfc.width) || (y >= (int)surface.sfc.height))
210     return;
211
212   c2rgba(c, &r, &g, &b, &a);
213
214   rgb2yuv(r, g, b, &Y, &U, &V);
215
216   remainder = (surface.sfc.width % 4);
217   if (remainder == 0)
218     line = surface.sfc.width;
219   else
220     line = surface.sfc.width + (4 - remainder);
221
222   offset = (y * line) + x;
223
224   *(surface.base[0] + offset) = Y;
225   *(surface.base[1] + (offset & 0xfffffffe)) = U;
226   *(surface.base[1] + (offset & 0xfffffffe) + 1) = V;
227   *(surface.base[2] + offset) = a;
228 }
229
230 void SurfaceMVP::drawPixel(int x, int y, Colour& c, bool fastdraw)
231 {
232   int offset;
233   unsigned char Y, U, V;
234   unsigned int line, remainder;
235
236   if ((x >= (int)surface.sfc.width) || (y >= (int)surface.sfc.height))
237     return;
238
239
240   rgb2yuv(c.red, c.green, c.blue, &Y, &U, &V);
241
242   remainder = (surface.sfc.width % 4);
243   if (remainder == 0)
244     line = surface.sfc.width;
245   else
246     line = surface.sfc.width + (4 - remainder);
247
248   offset = (y * line) + x;
249
250   *(surface.base[0] + offset) = Y;
251   *(surface.base[1] + (offset & 0xfffffffe)) = U;
252   *(surface.base[1] + (offset & 0xfffffffe) + 1) = V;
253   *(surface.base[2] + offset) = 0xff;
254 }
255  
256 void SurfaceMVP::drawHorzLine(int x1, int x2, int y, const DrawStyle& c)
257 {
258   fillblt(x1, y, x2-x1, 1, c);
259 }
260
261 void SurfaceMVP::drawVertLine(int x, int y1, int y2, const DrawStyle& c)
262 {
263   fillblt(x, y1, 1, y2-y1, c);
264 }
265
266 void SurfaceMVP::drawBitmap(int x, int y, const Bitmap& bm,const DisplayRegion & region) // region should not matter for SD
267 {
268   UINT bmw = bm.getWidth(); UINT bmh = bm.getHeight();
269   if (bmw == 0 || bmh == 0) return;
270   if ((x >= (int)surface.sfc.width) || (y >= (int)surface.sfc.height)) return;
271   int remainder = (surface.sfc.width % 4);
272   UINT line;
273   if (remainder == 0)
274     line = surface.sfc.width;
275   else
276     line = surface.sfc.width + (4 - remainder);
277   const std::vector<UCHAR>& bmdata = bm.rawData();
278   const std::vector<UCHAR>& Y = bm.palette.getYVector();
279   const std::vector<UCHAR>& Cr = bm.palette.getCrVector();
280   const std::vector<UCHAR>& Cb = bm.palette.getCbVector();
281   const std::vector<UCHAR>& A = bm.palette.getAVector();
282   UINT b_offset = 0;
283   UINT s_offset = x + y*line;
284   UINT plotWidth = bmw;
285   UINT plotHeight = bmh;
286   if (x + plotWidth - 1 > surface.sfc.width)
287     plotWidth = surface.sfc.width - x + 1;
288   if (y + plotHeight - 1 > surface.sfc.height)
289     plotHeight = surface.sfc.height - y + 1;
290   for (UINT j = 0; j < plotHeight; ++j)
291   {
292     UINT i = 0;
293     if (x & 1) // odd x - need to plot first column separately
294     {
295       UCHAR index = bmdata[b_offset];
296       *(surface.base[0] + s_offset) = Y[index];
297       *(surface.base[1] + s_offset - 1) = Cb[index];
298       *(surface.base[1] + s_offset) = Cr[index];
299       *(surface.base[2] + s_offset) = A[index];
300       i = 1;
301     }
302     // Now, plot pairs of pixels with averaged chroma values
303     while (i < plotWidth - 1)
304     {
305       UCHAR index1 = bmdata[b_offset + i];
306       UCHAR index2 = bmdata[b_offset + i + 1];
307       *(surface.base[0] + s_offset + i) = Y[index1];
308       *(surface.base[0] + s_offset + i + 1) = Y[index2];
309       *(surface.base[1] + s_offset + i) = (Cb[index1] + Cb[index2]) / 2;
310       *(surface.base[1] + s_offset + i + 1) = (Cr[index1] + Cr[index2]) / 2;
311       *(surface.base[2] + s_offset + i) = A[index1];
312       *(surface.base[2] + s_offset + i + 1) = A[index2];
313       i += 2;
314     }
315     if (i == plotWidth - 1) // One column left to do
316     {
317       UCHAR index = bmdata[b_offset + i];
318       *(surface.base[0] + s_offset + i) = Y[index];
319       *(surface.base[1] + s_offset + i) = Cb[index];
320       *(surface.base[1] + s_offset + i + 1) = Cr[index];
321       *(surface.base[2] + s_offset + i) = A[index];
322     }
323     s_offset += line;
324     b_offset += bmw;
325   }
326 }
327
328   /* surface update to screen needs:
329   source x distance into this surface
330   source y distance into this surface
331   width of update
332   height of update
333   destination x on screen
334   destination y on screen
335   */
336 int SurfaceMVP::updateToScreen(int sx, int sy, int w, int h, int dx, int dy) // FIXME new, replace others with this FIXME
337 {
338   return blt(fdOsd, surface.sfc.handle, sx, sy, w, h, ((SurfaceMVP*)screen)->getSurfaceHandle(), dx, dy);
339 }
340
341 int SurfaceMVP::blt(int fd, unsigned long shandle, int sx, int sy, int width, int height, unsigned long dhandle, int dx, int dy)
342 {
343   osd_bitblt_t fblt;
344   memset(&fblt, 0, sizeof(fblt));
345
346   fblt.dst_handle = dhandle;
347   fblt.dst_x = dx;
348   fblt.dst_y = dy;
349
350   fblt.src_handle = shandle;
351   fblt.src_x = sx;
352   fblt.src_y = sy;
353
354   fblt.width = width;
355   fblt.height = height;
356
357   fblt.u1 = 1;
358   fblt.u2 = 0;
359   fblt.u3 = 0x0f;
360
361   return ioctl(fd, GFX_FB_OSD_BITBLT, &fblt);
362 }
363
364 void SurfaceMVP::screenShot(const char* fileName)
365 {
366   Log* logger = Log::getInstance();
367
368   FILE* outfile = fopen(fileName, "w");
369   if (outfile == NULL)
370   {
371     logger->log("Surface", Log::ERR, "Can't open JPEG");
372     return;
373   }
374   logger->log("Surface", Log::DEBUG, "File opened %u %u", surface.sfc.height, surface.sfc.width);
375
376   struct jpeg_compress_struct cinfo;
377   struct jpeg_error_mgr jerr;
378   cinfo.err = jpeg_std_error(&jerr);
379   jpeg_create_compress(&cinfo);
380   jpeg_stdio_dest(&cinfo, outfile);
381   cinfo.image_width = surface.sfc.width;
382   cinfo.image_height = surface.sfc.height;
383   cinfo.input_components = 3;
384   cinfo.in_color_space = JCS_RGB;
385   jpeg_set_defaults(&cinfo);
386   jpeg_start_compress(&cinfo, TRUE);
387
388
389   unsigned char row[surface.sfc.width * 3];
390   unsigned char* prow = (unsigned char*)&row;
391   unsigned char r, g, b;
392
393   while (cinfo.next_scanline < cinfo.image_height)
394   {
395     for(unsigned int i = 0; i < surface.sfc.width; i++)
396     {
397       readPixel(i, cinfo.next_scanline, &r, &g, &b);
398       row[i * 3] = r;
399       row[(i * 3) + 1] = g;
400       row[(i * 3) + 2] = b;
401     }
402     jpeg_write_scanlines(&cinfo, (JSAMPLE **)&prow, 1);
403   }
404
405   jpeg_finish_compress(&cinfo);
406   jpeg_destroy_compress(&cinfo);
407   fclose(outfile);
408   logger->log("Surface", Log::DEBUG, "Jpeg saved");
409 }
410
411 void SurfaceMVP::readPixel(int x, int y, unsigned char* r, unsigned char* g, unsigned char* b)
412 {
413   int offset;
414   unsigned char a, Y, U, V;
415   unsigned int line, remainder;
416
417   if (((unsigned int)x >= surface.sfc.width) || ((unsigned int)y >= surface.sfc.height)) return;
418
419   remainder = (surface.sfc.width % 4);
420   if (remainder == 0)
421     line = surface.sfc.width;
422   else
423     line = surface.sfc.width + (4 - remainder);
424
425   offset = (y * line) + x;
426
427   Y = *(surface.base[0] + offset);
428   U = *(surface.base[1] + (offset & 0xfffffffe));
429   V = *(surface.base[1] + (offset & 0xfffffffe) + 1);
430   a = *(surface.base[2] + offset);
431
432   yuv2rgb(Y, U, V, r, g, b);
433 }
434
435 void SurfaceMVP::yuv2rgb(int y, int u, int v, unsigned char* pr, unsigned char* pg, unsigned char* pb)
436 {
437    // from http://www.fourcc.org/index.php?http%3A//www.fourcc.org/fccyvrgb.php
438
439 //   unsigned int pixel32;
440 //   unsigned char *pixel = (unsigned char *)&pixel32;
441    int r, g, b;
442
443
444    /*
445      One formula I found:  (not the right one)
446
447      R = 1.164(Y - 16) + 1.596(Cr - 128)
448      G = 1.164(Y - 16) - 0.813(Cr - 128) - 0.391(Cb - 128)
449      B = 1.164(Y - 16)                   + 2.018(Cb - 128)
450
451
452    r = (1.164 * (y - 16))
453       + (2.018 * (v - 128));
454    g = (1.164 * (y - 16))
455       - (0.813 * (u - 128))
456       - (0.391 * (v - 128));
457    b = (1.164 * (y - 16))
458       + (1.596 * (u - 128));
459
460
461      Another formula I found:  (seems to work)
462
463      R = Y + 1.370705 (V-128)
464      G = Y - 0.698001 (V-128) - 0.337633 (U-128)
465      B = Y + 1.732446 (U-128)
466    */
467
468    r = (int)( y + (1.370705 * (v-128)) );
469    g = (int)( y - (0.698001 * (v-128)) - (0.337633 * (u-128)) );
470    b = (int)( y + (1.732446 * (u-128)) );
471
472    // Even with proper conversion, some values still need clipping.
473    if (r > 255) r = 255;
474    if (g > 255) g = 255;
475    if (b > 255) b = 255;
476    if (r < 0) r = 0;
477    if (g < 0) g = 0;
478    if (b < 0) b = 0;
479
480    // Values only go from 0-220..  Why?
481 //   pixel[0] = r * 220 / 256;
482 //   pixel[1] = g * 220 / 256;
483 //   pixel[2] = b * 220 / 256;
484 //   pixel[3] = 0;
485
486    *pr = (unsigned char) (r * 220 / 256);
487    *pg = (unsigned char) (g * 220 / 256);
488    *pb = (unsigned char) (b * 220 / 256);
489
490    /* Debug
491    //printf("yuv2rgb(%i, %i, %i) -> %i, %i, %i  (0x%x)\n",
492     y, u, v,
493     pixel[0], pixel[1], pixel[2],
494     pixel32);
495    */
496
497 //   return pixel32;
498 }