Merge branch 'master' of fraxinas@git.opendreambox.org:/git/enigma2
[enigma2.git] / lib / gdi / gpixmap.cpp
1 #include <cstdlib>
2 #include <cstring>
3 #include <lib/gdi/gpixmap.h>
4 #include <lib/gdi/region.h>
5 #include <lib/gdi/accel.h>
6
7 gLookup::gLookup()
8         :size(0), lookup(0)
9 {
10 }
11
12 gLookup::gLookup(int size, const gPalette &pal, const gRGB &start, const gRGB &end)
13         :size(0), lookup(0)
14 {
15         build(size, pal, start, end);
16 }
17
18 void gLookup::build(int _size, const gPalette &pal, const gRGB &start, const gRGB &end)
19 {
20         if (lookup)
21         {
22                 delete [] lookup;
23                 lookup=0;
24                 size=0;
25         }
26         size=_size;
27         if (!size)
28                 return;
29         lookup=new gColor[size];
30         
31         for (int i=0; i<size; i++)
32         {
33                 gRGB col;
34                 if (i)
35                 {
36                         int rdiff=-start.r+end.r;
37                         int gdiff=-start.g+end.g;
38                         int bdiff=-start.b+end.b;
39                         int adiff=-start.a+end.a;
40                         rdiff*=i; rdiff/=(size-1);
41                         gdiff*=i; gdiff/=(size-1);
42                         bdiff*=i; bdiff/=(size-1);
43                         adiff*=i; adiff/=(size-1);
44                         col.r=start.r+rdiff;
45                         col.g=start.g+gdiff;
46                         col.b=start.b+bdiff;
47                         col.a=start.a+adiff;
48                 } else
49                         col=start;
50                 lookup[i]=pal.findColor(col);
51         }
52 }
53
54 gSurface::gSurface()
55 {
56         x = 0;
57         y = 0;
58         bpp = 0;
59         bypp = 0;
60         stride = 0;
61         data = 0;
62         data_phys = 0;
63         clut.colors = 0;
64         clut.data = 0;
65         type = 0;
66 }
67
68 gSurface::gSurface(eSize size, int _bpp, int accel)
69 {
70         x = size.width();
71         y = size.height();
72         bpp = _bpp;
73
74         switch (bpp)
75         {
76         case 8:
77                 bypp = 1;
78                 break;
79         case 15:
80         case 16:
81                 bypp = 2;
82                 break;
83         case 24:                // never use 24bit mode
84         case 32:
85                 bypp = 4;
86                 break;
87         default:
88                 bypp = (bpp+7)/8;
89         }
90
91         stride = x*bypp;
92         
93         data = 0;
94         data_phys = 0;
95         
96         if (accel)
97         {
98                 stride += 63;
99                 stride &=~63;
100                 
101                 if (gAccel::getInstance())
102                         eDebug("accel memory: %d", gAccel::getInstance()->accelAlloc(data, data_phys, y * stride));
103                 else
104                         eDebug("no accel available");
105         }
106         
107         clut.colors = 0;
108         clut.data = 0;
109
110         if (!data)
111                 data = new unsigned char [y * stride];
112         
113         type = 1;
114 }
115
116 gSurface::~gSurface()
117 {
118         if (type)
119         {
120                 if (data_phys)
121                         gAccel::getInstance()->accelFree(data_phys);
122                 else
123                         delete [] (unsigned char*)data;
124
125                 delete [] clut.data;
126         }
127 }
128
129 gPixmap *gPixmap::lock()
130 {
131         contentlock.lock(1);
132         return this;
133 }
134
135 void gPixmap::unlock()
136 {
137         contentlock.unlock(1);
138 }
139
140 void gPixmap::fill(const gRegion &region, const gColor &color)
141 {
142         unsigned int i;
143         for (i=0; i<region.rects.size(); ++i)
144         {
145                 const eRect &area = region.rects[i];
146                 if ((area.height()<=0) || (area.width()<=0))
147                         continue;
148
149                 if (surface->bpp == 8)
150                 {
151                         for (int y=area.top(); y<area.bottom(); y++)
152                                 memset(((__u8*)surface->data)+y*surface->stride+area.left(), color.color, area.width());
153                 } else if (surface->bpp == 32)
154                 {
155                         __u32 col;
156
157                         if (surface->clut.data && color < surface->clut.colors)
158                                 col=(surface->clut.data[color].a<<24)|(surface->clut.data[color].r<<16)|(surface->clut.data[color].g<<8)|(surface->clut.data[color].b);
159                         else
160                                 col=0x10101*color;
161                         
162                         col^=0xFF000000;                        
163
164                         if (surface->data_phys && gAccel::getInstance())
165                                 if (!gAccel::getInstance()->fill(surface,  area, col))
166                                         continue;
167
168                         for (int y=area.top(); y<area.bottom(); y++)
169                         {
170                                 __u32 *dst=(__u32*)(((__u8*)surface->data)+y*surface->stride+area.left()*surface->bypp);
171                                 int x=area.width();
172                                 while (x--)
173                                         *dst++=col;
174                         }
175                 }       else
176                         eWarning("couldn't fill %d bpp", surface->bpp);
177         }
178 }
179
180 void gPixmap::fill(const gRegion &region, const gRGB &color)
181 {
182         unsigned int i;
183         for (i=0; i<region.rects.size(); ++i)
184         {
185                 const eRect &area = region.rects[i];
186                 if ((area.height()<=0) || (area.width()<=0))
187                         continue;
188
189                 if (surface->bpp == 32)
190                 {
191                         __u32 col;
192
193                         col = color.argb();
194                         col^=0xFF000000;
195
196                         if (surface->data_phys && gAccel::getInstance())
197                                 if (!gAccel::getInstance()->fill(surface,  area, col))
198                                         continue;
199
200                         for (int y=area.top(); y<area.bottom(); y++)
201                         {
202                                 __u32 *dst=(__u32*)(((__u8*)surface->data)+y*surface->stride+area.left()*surface->bypp);
203                                 int x=area.width();
204                                 while (x--)
205                                         *dst++=col;
206                         }
207                 }       else
208                         eWarning("couldn't rgbfill %d bpp", surface->bpp);
209         }
210 }
211
212 static void blit_8i_to_32(__u32 *dst, __u8 *src, __u32 *pal, int width)
213 {
214         while (width--)
215                 *dst++=pal[*src++];
216 }
217
218 static void blit_8i_to_32_at(__u32 *dst, __u8 *src, __u32 *pal, int width)
219 {
220         while (width--)
221         {
222                 if (!(pal[*src]&0x80000000))
223                 {
224                         src++;
225                         dst++;
226                 } else
227                         *dst++=pal[*src++];
228         }
229 }
230
231                 /* WARNING, this function is not endian safe! */
232 static void blit_8i_to_32_ab(__u32 *dst, __u8 *src, __u32 *pal, int width)
233 {
234         while (width--)
235         {
236 #define BLEND(x, y, a) (y + (((x-y) * a)>>8))
237                 __u32 srccol = pal[*src++];
238                 __u32 dstcol = *dst;
239                 unsigned char sb = srccol & 0xFF;
240                 unsigned char sg = (srccol >> 8) & 0xFF;
241                 unsigned char sr = (srccol >> 16) & 0xFF;
242                 unsigned char sa = (srccol >> 24) & 0xFF;
243
244                 unsigned char db = dstcol & 0xFF;
245                 unsigned char dg = (dstcol >> 8) & 0xFF;
246                 unsigned char dr = (dstcol >> 16) & 0xFF;
247                 unsigned char da = (dstcol >> 24) & 0xFF;
248
249                 da = BLEND(0xFF, da, sa) & 0xFF;
250                 dr = BLEND(sr, dr, sa) & 0xFF;
251                 dg = BLEND(sg, dg, sa) & 0xFF;
252                 db = BLEND(sb, db, sa) & 0xFF;
253
254 #undef BLEND
255                 *dst++ = db | (dg << 8) | (dr << 16) | (da << 24);
256         }
257 }
258
259
260 void gPixmap::blit(const gPixmap &src, ePoint pos, const gRegion &clip, int flag)
261 {
262         for (unsigned int i=0; i<clip.rects.size(); ++i)
263         {
264                 eRect area=eRect(pos, src.size());
265                 area&=clip.rects[i];
266                 area&=eRect(ePoint(0, 0), size());
267
268                 if ((area.width()<0) || (area.height()<0))
269                         continue;
270
271                 eRect srcarea=area;
272                 srcarea.moveBy(-pos.x(), -pos.y());
273
274                 if ((surface->data_phys && src.surface->data_phys) && (gAccel::getInstance()))
275                         if (!gAccel::getInstance()->blit(surface, src.surface, area.topLeft(), srcarea, flag))
276                                 continue;
277
278                 if ((surface->bpp == 8) && (src.surface->bpp==8))
279                 {
280                         __u8 *srcptr=(__u8*)src.surface->data;
281                         __u8 *dstptr=(__u8*)surface->data;
282
283                         srcptr+=srcarea.left()*src.surface->bypp+srcarea.top()*src.surface->stride;
284                         dstptr+=area.left()*surface->bypp+area.top()*surface->stride;
285                         for (int y=0; y<area.height(); y++)
286                         {
287                                 if (flag & (blitAlphaTest|blitAlphaBlend))
288                                 {
289                       // no real alphatest yet
290                                         int width=area.width();
291                                         unsigned char *src=(unsigned char*)srcptr;
292                                         unsigned char *dst=(unsigned char*)dstptr;
293                                                 // use duff's device here!
294                                         while (width--)
295                                         {
296                                                 if (!*src)
297                                                 {
298                                                         src++;
299                                                         dst++;
300                                                 } else
301                                                         *dst++=*src++;
302                                         }
303                                 } else
304                                         memcpy(dstptr, srcptr, area.width()*surface->bypp);
305                                 srcptr+=src.surface->stride;
306                                 dstptr+=surface->stride;
307                         }
308                 } else if ((surface->bpp == 32) && (src.surface->bpp==32))
309                 {
310                         __u32 *srcptr=(__u32*)src.surface->data;
311                         __u32 *dstptr=(__u32*)surface->data;
312
313                         srcptr+=srcarea.left()+srcarea.top()*src.surface->stride/4;
314                         dstptr+=area.left()+area.top()*surface->stride/4;
315                         for (int y=0; y<area.height(); y++)
316                         {
317                                 if (flag & blitAlphaTest)
318                                 {
319                                         int width=area.width();
320                                         unsigned long *src=(unsigned long*)srcptr;
321                                         unsigned long *dst=(unsigned long*)dstptr;
322
323                                         while (width--)
324                                         {
325                                                 if (!((*src)&0xFF000000))
326                                                 {
327                                                         src++;
328                                                         dst++;
329                                                 } else
330                                                         *dst++=*src++;
331                                         }
332                                 } else if (flag & blitAlphaBlend)
333                                 {
334                                         // uh oh. this is only until hardware accel is working.
335
336                                         int width=area.width();
337                                                         // ARGB color space!
338                                         unsigned char *src=(unsigned char*)srcptr;
339                                         unsigned char *dst=(unsigned char*)dstptr;
340
341 #define BLEND(x, y, a) (y + ((x-y) * a)/256)
342                                         while (width--)
343                                         {
344                                                 unsigned char sa = src[3];
345                                                 unsigned char sr = src[2];
346                                                 unsigned char sg = src[1];
347                                                 unsigned char sb = src[0];
348
349                                                 unsigned char da = dst[3];
350                                                 unsigned char dr = dst[2];
351                                                 unsigned char dg = dst[1];
352                                                 unsigned char db = dst[0];
353
354                                                 dst[3] = BLEND(0xFF, da, sa);
355                                                 dst[2] = BLEND(sr, dr, sa);
356                                                 dst[1] = BLEND(sg, dg, sa);
357                                                 dst[0] = BLEND(sb, db, sa);
358 #undef BLEND
359
360                                                 src += 4; dst += 4;
361                                         }
362                                 } else
363                                         memcpy(dstptr, srcptr, area.width()*surface->bypp);
364                                 srcptr+=src.surface->stride/4;
365                                 dstptr+=surface->stride/4;
366                         }
367                 } else if ((surface->bpp == 32) && (src.surface->bpp==8))
368                 {       
369                         __u8 *srcptr=(__u8*)src.surface->data;
370                         __u8 *dstptr=(__u8*)surface->data; // !!
371                         __u32 pal[256];
372
373                         for (int i=0; i<256; ++i)
374                         {
375                                 if (src.surface->clut.data && (i<src.surface->clut.colors))
376                                         pal[i]=(src.surface->clut.data[i].a<<24)|(src.surface->clut.data[i].r<<16)|(src.surface->clut.data[i].g<<8)|(src.surface->clut.data[i].b);
377                                 else
378                                         pal[i]=0x010101*i;
379                                 pal[i]^=0xFF000000;
380                         }
381
382                         srcptr+=srcarea.left()*src.surface->bypp+srcarea.top()*src.surface->stride;
383                         dstptr+=area.left()*surface->bypp+area.top()*surface->stride;
384                         for (int y=0; y<area.height(); y++)
385                         {
386                                 int width=area.width();
387                                 unsigned char *psrc=(unsigned char*)srcptr;
388                                 __u32 *dst=(__u32*)dstptr;
389                                 if (flag & blitAlphaTest)
390                                         blit_8i_to_32_at(dst, psrc, pal, width);
391                                 else if (flag & blitAlphaBlend)
392                                         blit_8i_to_32_ab(dst, psrc, pal, width);
393                                 else
394                                         blit_8i_to_32(dst, psrc, pal, width);
395                                 srcptr+=src.surface->stride;
396                                 dstptr+=surface->stride;
397                         }
398                 } else
399                         eWarning("cannot blit %dbpp from %dbpp", surface->bpp, src.surface->bpp);
400         }
401 }
402
403 void gPixmap::mergePalette(const gPixmap &target)
404 {
405         if ((!surface->clut.colors) || (!target.surface->clut.colors))
406                 return;
407
408         gColor *lookup=new gColor[surface->clut.colors];
409
410         for (int i=0; i<surface->clut.colors; i++)
411                 lookup[i].color=target.surface->clut.findColor(surface->clut.data[i]);
412         
413         delete [] surface->clut.data;
414         surface->clut.colors=target.surface->clut.colors;
415         surface->clut.data=new gRGB[surface->clut.colors];
416         memcpy(surface->clut.data, target.surface->clut.data, sizeof(gRGB)*surface->clut.colors);
417
418         __u8 *dstptr=(__u8*)surface->data;
419
420         for (int ay=0; ay<surface->y; ay++)
421         {
422                 for (int ax=0; ax<surface->x; ax++)
423                         dstptr[ax]=lookup[dstptr[ax]];
424                 dstptr+=surface->stride;
425         }
426         
427         delete [] lookup;
428 }
429
430 static inline int sgn(int a)
431 {
432         if (a < 0)
433                 return -1;
434         else if (!a)
435                 return 0;
436         else
437                 return 1;
438 }
439
440 void gPixmap::line(const gRegion &clip, ePoint start, ePoint dst, gColor color)
441 {
442         __u8 *srf8 = 0;
443         __u32 *srf32 = 0; 
444         int stride = surface->stride;
445         
446         if (clip.rects.empty())
447                 return;
448                 
449         __u32 col = 0;
450         if (surface->bpp == 8)
451         {
452                 srf8 = (__u8*)surface->data;
453         } else if (surface->bpp == 32)
454         {
455                 srf32 = (__u32*)surface->data;
456                 
457                 if (surface->clut.data && color < surface->clut.colors)
458                         col=(surface->clut.data[color].a<<24)|(surface->clut.data[color].r<<16)|(surface->clut.data[color].g<<8)|(surface->clut.data[color].b);
459                 else
460                         col=0x10101*color;
461                 col^=0xFF000000;                        
462         }
463         
464         int xa = start.x(), ya = start.y(), xb = dst.x(), yb = dst.y();
465         int dx, dy, x, y, s1, s2, e, temp, swap, i;
466         dy=abs(yb-ya);
467         dx=abs(xb-xa);
468         s1=sgn(xb-xa);
469         s2=sgn(yb-ya);
470         x=xa;
471         y=ya;
472         if (dy>dx)
473         {
474                 temp=dx;
475                 dx=dy;
476                 dy=temp;
477                 swap=1;
478         } else
479                 swap=0;
480         e = 2*dy-dx;
481         
482         int lasthit = 0;
483         for(i=1; i<=dx; i++)
484         {
485                                 /* i don't like this clipping loop, but the only */
486                                 /* other choice i see is to calculate the intersections */
487                                 /* before iterating through the pixels. */
488                                 
489                                 /* one could optimize this because of the ordering */
490                                 /* of the bands. */
491                                 
492                 lasthit = 0;
493                 int a = lasthit;
494                 
495                         /* if last pixel was invisble, first check bounding box */
496                 if (a == -1)
497                 {
498                                 /* check if we just got into the bbox again */
499                         if (clip.extends.contains(x, y))
500                                 lasthit = a = 0;
501                         else
502                                 goto fail;
503                 } else if (!clip.rects[a].contains(x, y))
504                 {
505                         do
506                         {
507                                 ++a;
508                                 if ((unsigned int)a == clip.rects.size())
509                                         a = 0;
510                                 if (a == lasthit)
511                                 {
512                                         goto fail;
513                                         lasthit = -1;
514                                 }
515                         } while (!clip.rects[a].contains(x, y));
516                         lasthit = a;
517                 }
518                 
519                 if (srf8)
520                         srf8[y * stride + x] = color;
521                 if (srf32)
522                         srf32[y * stride/4 + x] = col;
523 fail:
524                 while (e>=0)
525                 {
526                         if (swap==1) x+=s1;
527                         else y+=s2;
528                         e-=2*dx;
529                 }
530     if (swap==1)
531         y+=s2;
532                 else
533                         x+=s1;
534                 e+=2*dy;
535         }
536 }
537
538 gColor gPalette::findColor(const gRGB &rgb) const
539 {
540                 /* grayscale? */
541         if (!data)
542                 return (rgb.r + rgb.g + rgb.b) / 3;
543         
544         int difference=1<<30, best_choice=0;
545         for (int t=0; t<colors; t++)
546         {
547                 int ttd;
548                 int td=(signed)(rgb.r-data[t].r); td*=td; td*=(255-data[t].a);
549                 ttd=td;
550                 if (ttd>=difference)
551                         continue;
552                 td=(signed)(rgb.g-data[t].g); td*=td; td*=(255-data[t].a);
553                 ttd+=td;
554                 if (ttd>=difference)
555                         continue;
556                 td=(signed)(rgb.b-data[t].b); td*=td; td*=(255-data[t].a);
557                 ttd+=td;
558                 if (ttd>=difference)
559                         continue;
560                 td=(signed)(rgb.a-data[t].a); td*=td; td*=255;
561                 ttd+=td;
562                 if (ttd>=difference)
563                         continue;
564                 if (!ttd)
565                         return t;
566                 difference=ttd;
567                 best_choice=t;
568         }
569         return best_choice;
570 }
571
572 DEFINE_REF(gPixmap);
573
574 gPixmap::~gPixmap()
575 {
576         if (must_delete_surface)
577                 delete surface;
578 }
579
580 gPixmap::gPixmap(gSurface *surface)
581         :surface(surface), must_delete_surface(false)
582 {
583 }
584
585 gPixmap::gPixmap(eSize size, int bpp, int accel)
586         :must_delete_surface(true)
587 {
588         surface = new gSurface(size, bpp, accel);
589 }
590