]> git.sesse.net Git - vlc/blob - modules/gui/beos/DrawingTidbits.cpp
Removes trailing spaces. Removes tabs.
[vlc] / modules / gui / beos / DrawingTidbits.cpp
1 /*****************************************************************************
2  * DrawingTidbits.cpp
3  *****************************************************************************
4  * Copyright (C) 2001 the VideoLAN team
5  * $Id$
6  *
7  * Authors: Tony Castley <tcastley@mail.powerup.com.au>
8  *          Stephan Aßmus <stippi@yellowbites.com>
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23  *****************************************************************************/
24
25 #include <math.h>
26
27 #include <Bitmap.h>
28 #include <Debug.h>
29 #include <Screen.h>
30
31 #include "DrawingTidbits.h"
32
33 // ShiftComponent
34 inline uchar
35 ShiftComponent(uchar component, float percent)
36 {
37     // change the color by <percent>, make sure we aren't rounding
38     // off significant bits
39     if (percent >= 1)
40         return (uchar)(component * (2 - percent));
41     else
42         return (uchar)(255 - percent * (255 - component));
43 }
44
45 // ShiftColor
46 rgb_color
47 ShiftColor(rgb_color color, float percent)
48 {
49     rgb_color result = {
50         ShiftComponent(color.red, percent),
51         ShiftComponent(color.green, percent),
52         ShiftComponent(color.blue, percent),
53         0
54     };
55     
56     return result;
57 }
58
59 // CompareColors
60 static bool
61 CompareColors(const rgb_color a, const rgb_color b)
62 {
63     return a.red == b.red
64         && a.green == b.green
65         && a.blue == b.blue
66         && a.alpha == b.alpha;
67 }
68
69 // ==
70 bool
71 operator==(const rgb_color &a, const rgb_color &b)
72 {
73     return CompareColors(a, b);
74 }
75
76 // !=
77 bool
78 operator!=(const rgb_color &a, const rgb_color &b)
79 {
80     return !CompareColors(a, b);
81 }
82
83 // ReplaceColor
84 void
85 ReplaceColor(BBitmap *bitmap, rgb_color from, rgb_color to)
86 {
87     ASSERT(bitmap->ColorSpace() == B_COLOR_8_BIT); // other color spaces not implemented yet
88     
89     BScreen screen(B_MAIN_SCREEN_ID);
90     uint32 fromIndex = screen.IndexForColor(from);
91     uint32 toIndex = screen.IndexForColor(to);
92     
93     uchar *bits = (uchar *)bitmap->Bits();
94     int32 bitsLength = bitmap->BitsLength();    
95     for (int32 index = 0; index < bitsLength; index++)
96         if (bits[index] == fromIndex)
97             bits[index] = toIndex;
98 }
99
100 // ReplaceTransparentColor
101 void
102 ReplaceTransparentColor(BBitmap *bitmap, rgb_color with)
103 {
104     ASSERT(bitmap->ColorSpace() == B_COLOR_8_BIT); // other color spaces not implemented yet
105     
106     BScreen screen(B_MAIN_SCREEN_ID);
107     uint32 withIndex = screen.IndexForColor(with);
108     
109     uchar *bits = (uchar *)bitmap->Bits();
110     int32 bitsLength = bitmap->BitsLength();    
111     for (int32 index = 0; index < bitsLength; index++)
112         if (bits[index] == B_TRANSPARENT_8_BIT)
113             bits[index] = withIndex;
114 }
115
116 // ycrcb_to_rgb
117 inline void
118 ycbcr_to_rgb( uint8 y, uint8 cb, uint8 cr,
119               uint8& r, uint8& g, uint8& b)
120 {
121     r = (uint8)max_c( 0, min_c( 255, 1.164 * ( y - 16 ) + 1.596 * ( cr - 128 ) ) );
122     g = (uint8)max_c( 0, min_c( 255, 1.164 * ( y - 16 ) - 0.813 * ( cr - 128 )
123                                 - 0.391 * ( cb - 128 ) ) );
124     b = (uint8)max_c( 0, min_c( 255, 1.164 * ( y - 16 ) + 2.018 * ( cb - 128 ) ) );
125 }
126
127 // this function will not produce visually pleasing results!
128 // we'd have to convert to Lab colorspace, do the mixing
129 // and convert back to RGB - in an ideal world...
130 //
131 // mix_colors
132 inline void
133 mix_colors( uint8 ra, uint8 ga, uint8 ba,
134             uint8 rb, uint8 gb, uint8 bb,
135             uint8& r, uint8& g, uint8& b, float mixLevel )
136 {
137     float mixA = ( 1.0 - mixLevel );
138     float mixB = mixLevel;
139     r = (uint8)(mixA * ra + mixB * rb);
140     g = (uint8)(mixA * ga + mixB * gb);
141     b = (uint8)(mixA * ba + mixB * bb);
142 }
143
144 // the algorithm used is probably pretty slow, but it should be easy
145 // to understand what's going on...
146 //
147 // scale_bitmap
148 status_t
149 scale_bitmap( BBitmap* bitmap, uint32 fromWidth, uint32 fromHeight )
150 {
151     status_t status = B_BAD_VALUE;
152     
153     if ( bitmap && bitmap->IsValid()
154          && ( bitmap->ColorSpace() == B_RGB32 || bitmap->ColorSpace() == B_RGBA32 ) )
155     {
156         status = B_MISMATCHED_VALUES;
157         // we only support upscaling as of now
158         uint32 destWidth = bitmap->Bounds().IntegerWidth() + 1;
159         uint32 destHeight = bitmap->Bounds().IntegerHeight() + 1;
160         if ( fromWidth <= destWidth && fromHeight <= destHeight )
161         {
162             status = B_OK;
163             uint32 bpr = bitmap->BytesPerRow();
164             if ( fromWidth < destWidth )
165             {
166                 // scale horizontally
167                 uint8* src = (uint8*)bitmap->Bits();
168                 uint8* p = new uint8[fromWidth * 4];    // temp buffer
169                 for ( uint32 y = 0; y < fromHeight; y++ )
170                 {
171                     // copy valid pixels into temp buffer
172                     memcpy( p, src, fromWidth * 4 );
173                     for ( uint32 x = 0; x < destWidth; x++ )
174                     {
175                         // mix colors of left and right pixels and write it back
176                         // into the bitmap
177                         float xPos = ( (float)x / (float)destWidth ) * (float)fromWidth;
178                         uint32 leftIndex = (uint32)floorf( xPos ) * 4;
179                         uint32 rightIndex = (uint32)ceilf( xPos ) * 4;
180                         rgb_color left;
181                         left.red = p[leftIndex + 2];
182                         left.green = p[leftIndex + 1];
183                         left.blue = p[leftIndex + 0];
184                         rgb_color right;
185                         right.red = p[rightIndex + 2];
186                         right.green = p[rightIndex + 1];
187                         right.blue = p[rightIndex + 0];
188                         rgb_color mix;
189                         mix_colors( left.red, left.green, left.blue,
190                                     right.red, right.green, right.blue,
191                                     mix.red, mix.green, mix.blue, xPos - floorf( xPos ) );
192                         uint32 destIndex = x * 4;
193                         src[destIndex + 2] = mix.red;
194                         src[destIndex + 1] = mix.green;
195                         src[destIndex + 0] = mix.blue;
196                     }
197                     src += bpr;
198                 }
199                 delete[] p;
200             }
201             if ( fromHeight < destHeight )
202             {
203                 // scale vertically
204                 uint8* src = (uint8*)bitmap->Bits();
205                 uint8* p = new uint8[fromHeight * 3];    // temp buffer
206                 for ( uint32 x = 0; x < destWidth; x++ )
207                 {
208                     // copy valid pixels into temp buffer
209                     for ( uint32 y = 0; y < fromHeight; y++ )
210                     {
211                         uint32 destIndex = y * 3;
212                         uint32 srcIndex = x * 4 + y * bpr;
213                         p[destIndex + 0] = src[srcIndex + 0];
214                         p[destIndex + 1] = src[srcIndex + 1];
215                         p[destIndex + 2] = src[srcIndex + 2];
216                     }
217                     // do the scaling
218                     for ( uint32 y = 0; y < destHeight; y++ )
219                     {
220                         // mix colors of upper and lower pixels and write it back
221                         // into the bitmap
222                         float yPos = ( (float)y / (float)destHeight ) * (float)fromHeight;
223                         uint32 upperIndex = (uint32)floorf( yPos ) * 3;
224                         uint32 lowerIndex = (uint32)ceilf( yPos ) * 3;
225                         rgb_color upper;
226                         upper.red = p[upperIndex + 2];
227                         upper.green = p[upperIndex + 1];
228                         upper.blue = p[upperIndex + 0];
229                         rgb_color lower;
230                         lower.red = p[lowerIndex + 2];
231                         lower.green = p[lowerIndex + 1];
232                         lower.blue = p[lowerIndex + 0];
233                         rgb_color mix;
234                         mix_colors( upper.red, upper.green, upper.blue,
235                                     lower.red, lower.green, lower.blue,
236                                     mix.red, mix.green, mix.blue, yPos - floorf( yPos ) );
237                         uint32 destIndex = x * 4 + y * bpr;
238                         src[destIndex + 2] = mix.red;
239                         src[destIndex + 1] = mix.green;
240                         src[destIndex + 0] = mix.blue;
241                     }
242                 }
243                 delete[] p;
244             }
245         }
246     }
247     return status;
248 }
249
250 // convert_bitmap
251 status_t
252 convert_bitmap( BBitmap* inBitmap, BBitmap* outBitmap )
253 {
254     status_t status = B_BAD_VALUE;
255     // see that we got valid bitmaps
256     if ( inBitmap && inBitmap->IsValid()
257          && outBitmap && outBitmap->IsValid() )
258     {
259         status = B_MISMATCHED_VALUES;
260         // see that bitmaps are compatible and that we support the conversion
261         if ( inBitmap->Bounds().Width() <= outBitmap->Bounds().Width()
262              && inBitmap->Bounds().Height() <= outBitmap->Bounds().Height()
263              && ( outBitmap->ColorSpace() == B_RGB32
264                   || outBitmap->ColorSpace() == B_RGBA32) )
265         {
266             int32 width = inBitmap->Bounds().IntegerWidth() + 1;
267             int32 height = inBitmap->Bounds().IntegerHeight() + 1;
268             int32 srcBpr = inBitmap->BytesPerRow();
269             int32 dstBpr = outBitmap->BytesPerRow();
270             uint8* srcBits = (uint8*)inBitmap->Bits();
271             uint8* dstBits = (uint8*)outBitmap->Bits();
272             switch (inBitmap->ColorSpace())
273             {
274                 case B_YCbCr422:
275                     // Y0[7:0]  Cb0[7:0]  Y1[7:0]  Cr0[7:0]
276                     // Y2[7:0]  Cb2[7:0]  Y3[7:0]  Cr2[7:0]
277                     for ( int32 y = 0; y < height; y++ )
278                     {
279                         for ( int32 x = 0; x < width; x += 2 )
280                         {
281                             int32 srcOffset = x * 2;
282                             int32 dstOffset = x * 4;
283                             ycbcr_to_rgb( srcBits[srcOffset + 0],
284                                           srcBits[srcOffset + 1],
285                                           srcBits[srcOffset + 3],
286                                           dstBits[dstOffset + 2],
287                                           dstBits[dstOffset + 1],
288                                           dstBits[dstOffset + 0] );
289                             ycbcr_to_rgb( srcBits[srcOffset + 2],
290                                           srcBits[srcOffset + 1],
291                                           srcBits[srcOffset + 3],
292                                           dstBits[dstOffset + 6],
293                                           dstBits[dstOffset + 5],
294                                           dstBits[dstOffset + 4] );
295                             // take care of alpha
296                             dstBits[x * 4 + 3] = 255;
297                             dstBits[x * 4 + 7] = 255;
298                         }
299                         srcBits += srcBpr;
300                         dstBits += dstBpr;
301                     }
302                     status = B_OK;
303                     break;
304                 case B_YCbCr420:
305                     // Non-interlaced only!
306                     // Cb0  Y0  Y1  Cb2 Y2  Y3  on even scan lines ...
307                     // Cr0  Y0  Y1  Cr2 Y2  Y3  on odd scan lines
308                     status = B_ERROR;
309                     break;
310                 case B_YUV422:
311                     // U0[7:0]  Y0[7:0]   V0[7:0]  Y1[7:0]
312                     // U2[7:0]  Y2[7:0]   V2[7:0]  Y3[7:0]
313                     status = B_ERROR;
314                     break;
315                 case B_RGB32:
316                 case B_RGBA32:
317                     memcpy( dstBits, srcBits, inBitmap->BitsLength() );
318                     status = B_OK;
319                     break;
320                 case B_RGB16:
321                     // G[2:0],B[4:0]  R[4:0],G[5:3]
322                     for ( int32 y = 0; y < height; y ++ )
323                     {
324                         for ( int32 x = 0; x < width; x++ )
325                         {
326                             int32 srcOffset = x * 2;
327                             int32 dstOffset = x * 4;
328                             uint8 blue = srcBits[srcOffset + 0] & 0x1f;
329                             uint8 green = ( srcBits[srcOffset + 0] >> 5 )
330                                           | ( ( srcBits[srcOffset + 1] & 0x07 ) << 3 );
331                             uint8 red = srcBits[srcOffset + 1] & 0xf8;
332                             // homogeneously scale each component to 8 bit
333                             dstBits[dstOffset + 0] = (blue << 3) | (blue >> 2);
334                             dstBits[dstOffset + 1] = (green << 2) | (green >> 4);
335                             dstBits[dstOffset + 2] = red | (red >> 5);
336                         }
337                         srcBits += srcBpr;
338                         dstBits += dstBpr;
339                     }
340                     status = B_OK;
341                     break;
342                 default:
343                     status = B_MISMATCHED_VALUES;
344                     break;
345             }
346             if ( status == B_OK )
347             {
348                 if ( width < outBitmap->Bounds().IntegerWidth() + 1
349                      || height < outBitmap->Bounds().IntegerHeight() + 1 )
350                 {
351                     scale_bitmap( outBitmap, width, height );
352                 }
353             }
354         }
355     }
356     return status;
357 }
358
359 // clip_float
360 inline uint8
361 clip_float(float value)
362 {
363     if (value < 0)
364         value = 0;
365     if (value > 255)
366         value = 255;
367     return (uint8)value;
368 }
369
370 // dim_bitmap
371 status_t
372 dim_bitmap(BBitmap* bitmap, rgb_color center, float dimLevel)
373 {
374     status_t status = B_BAD_VALUE;
375     if (bitmap && bitmap->IsValid())
376     {
377         switch (bitmap->ColorSpace())
378         {
379             case B_CMAP8:
380             {
381                 BScreen screen(B_MAIN_SCREEN_ID);
382                 if (screen.IsValid())
383                 {
384                     // iterate over each pixel, get the respective
385                     // color from the screen object, find the distance
386                     // to the "center" color and shorten the distance
387                     // by "dimLevel"
388                     int32 length = bitmap->BitsLength();
389                     uint8* bits = (uint8*)bitmap->Bits();
390                     for (int32 i = 0; i < length; i++)
391                     {
392                         // preserve transparent pixels
393                         if (bits[i] != B_TRANSPARENT_MAGIC_CMAP8)
394                         {
395                             // get color for this index
396                             rgb_color c = screen.ColorForIndex(bits[i]);
397                             // red
398                             float dist = (c.red - center.red) * dimLevel;
399                             c.red = clip_float(center.red + dist);
400                             // green
401                             dist = (c.green - center.green) * dimLevel;
402                             c.green = clip_float(center.green + dist);
403                             // blue
404                             dist = (c.blue - center.blue) * dimLevel;
405                             c.blue = clip_float(center.blue + dist);
406                             // write correct index of the dimmed color
407                             // back into bitmap (and hope the match is close...)
408                             bits[i] = screen.IndexForColor(c);
409                         }
410                     }
411                     status = B_OK;
412                 }
413                 break;
414             }
415             case B_RGB32:
416             case B_RGBA32:
417             {
418                 // iterate over each color component, find the distance
419                 // to the "center" color and shorten the distance
420                 // by "dimLevel"
421                 uint8* bits = (uint8*)bitmap->Bits();
422                 int32 bpr = bitmap->BytesPerRow();
423                 int32 pixels = bitmap->Bounds().IntegerWidth() + 1;
424                 int32 lines = bitmap->Bounds().IntegerHeight() + 1;
425                 // iterate over color components
426                 for (int32 y = 0; y < lines; y++) {
427                     for (int32 x = 0; x < pixels; x++) {
428                         int32 offset = 4 * x; // four bytes per pixel
429                         // blue
430                         float dist = (bits[offset + 0] - center.blue) * dimLevel;
431                         bits[offset + 0] = clip_float(center.blue + dist);
432                         // green
433                         dist = (bits[offset + 1] - center.green) * dimLevel;
434                         bits[offset + 1] = clip_float(center.green + dist);
435                         // red
436                         dist = (bits[offset + 2] - center.red) * dimLevel;
437                         bits[offset + 2] = clip_float(center.red + dist);
438                         // ignore alpha channel
439                     }
440                     // next line
441                     bits += bpr;
442                 }
443                 status = B_OK;
444                 break;
445             }
446             default:
447                 status = B_ERROR;
448                 break;
449         }
450     }
451     return status;
452 }
453
454 // dimmed_color_cmap8
455 rgb_color
456 dimmed_color_cmap8(rgb_color color, rgb_color center, float dimLevel)
457 {
458     BScreen screen(B_MAIN_SCREEN_ID);
459     if (screen.IsValid())
460     {
461         // red
462         float dist = (color.red - center.red) * dimLevel;
463         color.red = clip_float(center.red + dist);
464         // green
465         dist = (color.green - center.green) * dimLevel;
466         color.green = clip_float(center.green + dist);
467         // blue
468         dist = (color.blue - center.blue) * dimLevel;
469         color.blue = clip_float(center.blue + dist);
470         // get color index for dimmed color
471         int32 index = screen.IndexForColor(color);
472         // put color at index (closest match in palette
473         // to dimmed result) into returned color
474         color = screen.ColorForIndex(index);
475     }
476     return color;
477 }