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