nux-1.14.0
CairoGraphics.cpp
00001 /*
00002  * Copyright 2010 Inalogic® Inc.
00003  *
00004  * This program is free software: you can redistribute it and/or modify it
00005  * under the terms of the GNU Lesser General Public License, as
00006  * published by the  Free Software Foundation; either version 2.1 or 3.0
00007  * of the License.
00008  *
00009  * This program is distributed in the hope that it will be useful, but
00010  * WITHOUT ANY WARRANTY; without even the implied warranties of
00011  * MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR
00012  * PURPOSE.  See the applicable version of the GNU Lesser General Public
00013  * License for more details.
00014  *
00015  * You should have received a copy of both the GNU Lesser General Public
00016  * License along with this program. If not, see <http://www.gnu.org/licenses/>
00017  *
00018  * Authored by: Jay Taoko <jaytaoko@inalogic.com>
00019  *
00020  */
00021 
00022 
00023 #include "NuxCore/NuxCore.h"
00024 #include "NuxCore/Rect.h"
00025 #include "BitmapFormats.h"
00026 #include "CairoGraphics.h"
00027 
00028 namespace nux
00029 {
00030 
00031   CairoGraphics::CairoGraphics (cairo_format_t format, int width, int height)
00032     :   _width (0)
00033     ,   _height (0)
00034   {
00035     nuxAssert (width >= 0);
00036     nuxAssert (height >= 0);
00037 
00038     _width = width;
00039     _height = height;
00040 
00041     if (_width <= 0)
00042       _width = 1;
00043 
00044     if (_height <= 0)
00045       _height = 1;
00046 
00047     _cairo_surface = cairo_image_surface_create (format, _width, _height);
00048     m_surface_format = format;
00049 
00050     _cr = cairo_create (_cairo_surface);
00051     if (cairo_status (_cr) == CAIRO_STATUS_NO_MEMORY)
00052     {
00053       nuxAssertMsg (0, TEXT ("[CairoGraphics::GetContext] Cairo context error.") );
00054     }
00055 
00056     _opacity = 1.0f;
00057     _zoom = 1.0;
00058   }
00059 
00060   CairoGraphics::~CairoGraphics()
00061   {
00062     cairo_destroy (_cr);
00063     cairo_surface_destroy (_cairo_surface);
00064   }
00065 
00066   cairo_t *CairoGraphics::GetContext()
00067   { 
00068     cairo_t *cr = cairo_create (_cairo_surface);
00069     if (cairo_status (cr) == CAIRO_STATUS_NO_MEMORY)
00070     {
00071       nuxAssertMsg (0, TEXT ("[CairoGraphics::GetContext] Cairo context error.") );
00072     }
00073     return cr;
00074   }
00075 
00076   cairo_t *CairoGraphics::GetInternalContext()
00077   { 
00078     return _cr;
00079   }
00080 
00081   cairo_surface_t* CairoGraphics::GetSurface ()
00082   {
00083     return _cairo_surface;
00084   }
00085 
00086   NBitmapData *CairoGraphics::GetBitmap()
00087   {
00088     if ( (_width <= 0) || (_height <= 0) )
00089     {
00090       nuxDebugMsg (TEXT ("[CairoGraphics::GetBitmap] Invalid surface.") );
00091     }
00092 
00093     NUX_RETURN_VALUE_IF_NULL (_width, 0);
00094     NUX_RETURN_VALUE_IF_NULL (_height, 0);
00095 
00096     BitmapFormat bitmap_format = BITFMT_UNKNOWN;
00097 
00098     if (m_surface_format == CAIRO_FORMAT_ARGB32)
00099     {
00100       // Each pixel is a 32-bit quantity, with alpha in the upper 8 bits,
00101       // then red, then green, then blue. The 32-bit quantities are stored native-endian.
00102       // Pre-multiplied alpha is used. (That is, 50% transparent red is 0x80800000, not 0x80ff0000.)
00103       bitmap_format = BITFMT_B8G8R8A8;
00104     }
00105 
00106     if (m_surface_format == CAIRO_FORMAT_RGB24)
00107     {
00108       // Each pixel is a 32-bit quantity, with the upper 8 bits unused.
00109       // Red, Green, and Blue are stored in the remaining 24 bits in that order.
00110       bitmap_format = BITFMT_B8G8R8A8;
00111     }
00112 
00113     if (m_surface_format == CAIRO_FORMAT_A8)
00114     {
00115       // Each pixel is a 8-bit quantity holding an alpha value.
00116       bitmap_format = BITFMT_A8;
00117     }
00118 
00119     if (m_surface_format == CAIRO_FORMAT_A1)
00120       bitmap_format = BITFMT_A8;
00121 
00122     NTextureData *bitmap_data = new NTextureData (bitmap_format, _width, _height, 1);
00123     t_u8 *ptr = cairo_image_surface_get_data (_cairo_surface);
00124     int stride = cairo_image_surface_get_stride (_cairo_surface);
00125 
00126     if (ptr == NULL || stride == 0)
00127     {
00128       // _cairo_surface is not a valid surface
00129       nuxError (TEXT ("[CairoGraphics::GetBitmap] Invalid surface"));
00130       return bitmap_data; // just returns because we will segfault otherwise
00131     }
00132 
00133     if (m_surface_format == CAIRO_FORMAT_A1)
00134     {
00135       t_u8 *temp = new t_u8[bitmap_data->GetSurface (0).GetPitch() ];
00136 
00137       for (int j = 0; j < _height; j++)
00138       {
00139         for (int i = 0; i < _width; i++)
00140         {
00141           // Get the byte
00142           int a = ptr[j * stride + i/8];
00143           // Get the position in the byte
00144           int b = (i - 8 * (i / 8) );
00145           // Shift the byte and get the last bit
00146           int c = (a >> b) & 0x1;
00147           // If the last bit is set, put 1, otherwise put 0
00148           temp[i] = c ? 0xFF : 0x0;
00149         }
00150 
00151         Memcpy ( bitmap_data->GetSurface (0).GetPtrRawData() + j * bitmap_data->GetSurface (0).GetPitch(),
00152                  (const void *) (&temp[0]),
00153                  _width);
00154       }
00155     }
00156     else
00157     {
00158       for (int j = 0; j < _height; j++)
00159       {
00160         Memcpy (bitmap_data->GetSurface (0).GetPtrRawData() + j * bitmap_data->GetSurface (0).GetPitch(),
00161                 (const void *) (&ptr[j * stride]),
00162                 _width * GPixelFormats[bitmap_format].NumComponents);
00163       }
00164     }
00165 
00166     return bitmap_data;
00167   }
00168 
00169   int CairoGraphics::GetWidth () const
00170   {
00171     return _width;
00172   }
00173 
00174   int CairoGraphics::GetHeight () const
00175   {
00176     return _height;
00177   }
00178 
00179   bool CairoGraphics::PushState ()
00180   {
00181     nuxAssert(_cr);
00182     _opacity_stack.push(_opacity);
00183     cairo_save(_cr);
00184     return true;
00185   }
00186 
00187   bool CairoGraphics::PopState ()
00188   {
00189     nuxAssert(_cr);
00190     if (_opacity_stack.empty())
00191     {
00192       return false;
00193     }
00194 
00195     _opacity = _opacity_stack.top();
00196     _opacity_stack.pop();
00197     cairo_restore(_cr);
00198     return true;
00199   }
00200 
00201   bool CairoGraphics::ClearCanvas ()
00202   {
00203     // Clear the surface.
00204     nuxAssert(_cr);
00205     cairo_operator_t op = cairo_get_operator(_cr);
00206     cairo_set_operator(_cr, CAIRO_OPERATOR_CLEAR);
00207     cairo_paint(_cr);
00208     cairo_set_operator(_cr, op);
00209 
00210     // Set the clip region to an infinitely large shape containing the target.
00211     cairo_reset_clip(_cr);
00212 
00213     _opacity = 1.0f;
00214     _opacity_stack = std::stack<float>();
00215 
00216     cairo_restore(_cr);
00217     cairo_save(_cr);
00218 
00219     return true;
00220   }
00221 
00222   bool CairoGraphics::ClearRect(double x, double y, double w, double h)
00223   {
00224     nuxAssert(_cr);
00225     cairo_rectangle(_cr, x, y, w, h);
00226     cairo_operator_t op = cairo_get_operator(_cr);
00227     cairo_set_operator(_cr, CAIRO_OPERATOR_CLEAR);
00228     cairo_fill(_cr);
00229     cairo_set_operator(_cr, op);
00230     return true;
00231   }
00232 
00233   bool CairoGraphics::DrawLine(double x0, double y0, double x1, double y1,
00234                                double width, const Color &c)
00235   {
00236     nuxAssert(_cr);
00237     if (width < 0.0)
00238     {
00239       return false;
00240     }
00241 
00242     cairo_set_line_width(_cr, width);
00243     cairo_set_source_rgba(_cr, c.red, c.green, c.blue, _opacity);
00244     cairo_move_to(_cr, x0, y0);
00245     cairo_line_to(_cr, x1, y1);
00246     cairo_stroke(_cr);
00247 
00248     return true;
00249   }
00250 
00251   bool CairoGraphics::DrawFilledRect(double x, double y, double w, double h,
00252                                      const Color &c)
00253   {
00254     nuxAssert(_cr);
00255     if (w <= 0.0 || h <= 0.0) {
00256       return false;
00257     }
00258 
00259     cairo_set_source_rgba(_cr, c.red, c.green, c.blue, _opacity);
00260     cairo_rectangle(_cr, x, y, w, h);
00261     cairo_fill(_cr);
00262     return true;
00263   }
00264 
00265   bool CairoGraphics::DrawCanvas(double x, double y, CairoGraphics *cg)
00266   {
00267     if (cg == 0) return false;
00268 
00269     cairo_surface_t *s = cg->GetSurface();
00270     double src_zoom = cg->_zoom;
00271     double inv_zoom = 1.0 / src_zoom;
00272 
00273     cairo_save(_cr);
00274 
00275     IntersectRectClipRegion(x, y, cg->GetWidth(), cg->GetHeight());
00276 
00277     cairo_scale(_cr, inv_zoom, inv_zoom);
00278     cairo_set_source_surface(_cr, s, x * src_zoom, y * src_zoom);
00279 
00280     cairo_pattern_set_extend(cairo_get_source(_cr), CAIRO_EXTEND_PAD);
00281 
00282     cairo_paint_with_alpha(_cr, _opacity);
00283     cairo_restore(_cr);
00284 
00285     return true;
00286   }
00287 
00288   static inline double
00289   _align (double val)
00290   {
00291     double fract = val - (int) val;
00292 
00293     if (fract != 0.5f)
00294       return (double) ((int) val + 0.5f);
00295     else
00296       return val;
00297   }
00298 
00299   bool
00300   CairoGraphics::DrawRoundedRectangle (cairo_t* cr,
00301                                        double   aspect,
00302                                        double   x,
00303                                        double   y,
00304                                        double   cornerRadius,
00305                                        double   width,
00306                                        double   height,
00307                                        bool     align)
00308   {
00309     double radius = cornerRadius / aspect;
00310 
00311     if (align)
00312     {
00313       // top-left, right of the corner
00314       cairo_move_to (cr, _align (x + radius), _align (y));
00315 
00316       // top-right, left of the corner
00317       cairo_line_to (cr, _align (x + width - radius), _align (y));
00318 
00319       // top-right, below the corner
00320       cairo_arc (cr,
00321                  _align (x + width - radius),
00322                  _align (y + radius),
00323                  radius,
00324                  -90.0f * G_PI / 180.0f,
00325                  0.0f * G_PI / 180.0f);
00326 
00327       // bottom-right, above the corner
00328       cairo_line_to (cr, _align (x + width), _align (y + height - radius));
00329 
00330       // bottom-right, left of the corner
00331       cairo_arc (cr,
00332                  _align (x + width - radius),
00333                  _align (y + height - radius),
00334                  radius,
00335                  0.0f * G_PI / 180.0f,
00336                  90.0f * G_PI / 180.0f);
00337 
00338       // bottom-left, right of the corner
00339       cairo_line_to (cr, _align (x + radius), _align (y + height));
00340 
00341       // bottom-left, above the corner
00342       cairo_arc (cr,
00343                  _align (x + radius),
00344                  _align (y + height - radius),
00345                  radius,
00346                  90.0f * G_PI / 180.0f,
00347                  180.0f * G_PI / 180.0f);
00348 
00349       // top-left, right of the corner
00350       cairo_arc (cr,
00351                  _align (x + radius),
00352                  _align (y + radius),
00353                  radius,
00354                  180.0f * G_PI / 180.0f,
00355                  270.0f * G_PI / 180.0f);
00356     }
00357     else
00358     {
00359       // top-left, right of the corner
00360       cairo_move_to (cr, x + radius, y);
00361 
00362       // top-right, left of the corner
00363       cairo_line_to (cr, x + width - radius, y);
00364 
00365       // top-right, below the corner
00366       cairo_arc (cr,
00367                  x + width - radius,
00368                  y + radius,
00369                  radius,
00370                  -90.0f * G_PI / 180.0f,
00371                  0.0f * G_PI / 180.0f);
00372 
00373       // bottom-right, above the corner
00374       cairo_line_to (cr, x + width, y + height - radius);
00375 
00376       // bottom-right, left of the corner
00377       cairo_arc (cr,
00378                  x + width - radius,
00379                  y + height - radius,
00380                  radius,
00381                  0.0f * G_PI / 180.0f,
00382                  90.0f * G_PI / 180.0f);
00383 
00384       // bottom-left, right of the corner
00385       cairo_line_to (cr, x + radius, y + height);
00386 
00387       // bottom-left, above the corner
00388       cairo_arc (cr,
00389                  x + radius,
00390                  y + height - radius,
00391                  radius,
00392                  90.0f * G_PI / 180.0f,
00393                  180.0f * G_PI / 180.0f);
00394 
00395       // top-left, right of the corner
00396       cairo_arc (cr,
00397                  x + radius,
00398                  y + radius,
00399                  radius,
00400                  180.0f * G_PI / 180.0f,
00401                  270.0f * G_PI / 180.0f);
00402     }
00403 
00404     return true;
00405   }
00406 
00407   static inline void _blurinner (guchar* pixel,
00408                                  gint*   zR,
00409                                  gint*   zG,
00410                                  gint*   zB,
00411                                  gint*   zA,
00412                                  gint    alpha,
00413                                  gint    aprec,
00414                                  gint    zprec)
00415   {
00416     gint R;
00417     gint G;
00418     gint B;
00419     guchar A;
00420 
00421     R = *pixel;
00422     G = *(pixel + 1);
00423     B = *(pixel + 2);
00424     A = *(pixel + 3);
00425 
00426     *zR += (alpha * ((R << zprec) - *zR)) >> aprec;
00427     *zG += (alpha * ((G << zprec) - *zG)) >> aprec;
00428     *zB += (alpha * ((B << zprec) - *zB)) >> aprec;
00429     *zA += (alpha * ((A << zprec) - *zA)) >> aprec;
00430 
00431     *pixel       = *zR >> zprec;
00432     *(pixel + 1) = *zG >> zprec;
00433     *(pixel + 2) = *zB >> zprec;
00434     *(pixel + 3) = *zA >> zprec;
00435   }
00436 
00437   static inline void _blurrow (guchar* pixels,
00438                                gint    width,
00439                                gint    height,
00440                                gint    channels,
00441                                gint    line,
00442                                gint    alpha,
00443                                gint    aprec,
00444                                gint    zprec)
00445   {
00446     gint    zR;
00447     gint    zG;
00448     gint    zB;
00449     gint    zA;
00450     gint    index;
00451     guchar* scanline;
00452 
00453     scanline = &(pixels[line * width * channels]);
00454 
00455     zR = *scanline << zprec;
00456     zG = *(scanline + 1) << zprec;
00457     zB = *(scanline + 2) << zprec;
00458     zA = *(scanline + 3) << zprec;
00459 
00460     for (index = 0; index < width; index ++)
00461       _blurinner (&scanline[index * channels], &zR, &zG, &zB, &zA, alpha, aprec,
00462       zprec);
00463 
00464     for (index = width - 2; index >= 0; index--)
00465       _blurinner (&scanline[index * channels], &zR, &zG, &zB, &zA, alpha, aprec,
00466       zprec);
00467   }
00468 
00469   static inline void _blurcol (guchar* pixels,
00470                                gint    width,
00471                                gint    height,
00472                                gint    channels,
00473                                gint    x,
00474                                gint    alpha,
00475                                gint    aprec,
00476                                gint    zprec)
00477   {
00478     gint zR;
00479     gint zG;
00480     gint zB;
00481     gint zA;
00482     gint index;
00483     guchar* ptr;
00484 
00485     ptr = pixels;
00486 
00487     ptr += x * channels;
00488 
00489     zR = *((guchar*) ptr    ) << zprec;
00490     zG = *((guchar*) ptr + 1) << zprec;
00491     zB = *((guchar*) ptr + 2) << zprec;
00492     zA = *((guchar*) ptr + 3) << zprec;
00493 
00494     for (index = width; index < (height - 1) * width; index += width)
00495       _blurinner ((guchar*) &ptr[index * channels], &zR, &zG, &zB, &zA, alpha,
00496       aprec, zprec);
00497 
00498     for (index = (height - 2) * width; index >= 0; index -= width)
00499       _blurinner ((guchar*) &ptr[index * channels], &zR, &zG, &zB, &zA, alpha,
00500       aprec, zprec);
00501   }
00502 
00503   //
00504   // pixels   image-data
00505   // width    image-width
00506   // height   image-height
00507   // channels image-channels
00508   //
00509   // in-place blur of image 'img' with kernel of approximate radius 'radius'
00510   //
00511   // blurs with two sided exponential impulse response
00512   //
00513   // aprec = precision of alpha parameter in fixed-point format 0.aprec
00514   //
00515   // zprec = precision of state parameters zR,zG,zB and zA in fp format 8.zprec
00516   //
00517   void _expblur (guchar* pixels,
00518                  gint    width,
00519                  gint    height,
00520                  gint    channels,
00521                  gint    radius,
00522                  gint    aprec,
00523                  gint    zprec)
00524   {
00525     gint alpha;
00526     gint row = 0;
00527     gint col = 0;
00528 
00529     if (radius < 1)
00530       return;
00531 
00532     // calculate the alpha such that 90% of 
00533     // the kernel is within the radius.
00534     // (Kernel extends to infinity)
00535     alpha = (gint) ((1 << aprec) * (1.0f - expf (-2.3f / (radius + 1.f))));
00536 
00537     for (; row < height; row++)
00538       _blurrow (pixels, width, height, channels, row, alpha, aprec, zprec);
00539 
00540     for(; col < width; col++)
00541       _blurcol (pixels, width, height, channels, col, alpha, aprec, zprec);
00542 
00543     return;
00544   }
00545 
00546   // if called like BlurSurface (radius) or BlurSurface (radius, NULL) it will
00547   // try to blur the image-surface of the internal cairo-context
00548   bool CairoGraphics::BlurSurface (unsigned int radius, cairo_surface_t* surf)
00549   {
00550     cairo_surface_t* surface;
00551     guchar*          pixels;
00552     guint            width;
00553     guint            height;
00554     cairo_format_t   format;
00555 
00556     if (surf)
00557       surface = surf;
00558     else
00559       surface = cairo_get_target (_cr);
00560 
00561     // don't do anything if we're not dealing with an image-surface
00562       if (cairo_surface_get_type (surface) != CAIRO_SURFACE_TYPE_IMAGE)
00563       return false;
00564 
00565     // before we mess with the surface execute any pending drawing
00566     cairo_surface_flush (surface);
00567 
00568     pixels = cairo_image_surface_get_data (surface);
00569     width  = cairo_image_surface_get_width (surface);
00570     height = cairo_image_surface_get_height (surface);
00571     format = cairo_image_surface_get_format (surface);
00572 
00573     switch (format)
00574     {
00575       case CAIRO_FORMAT_ARGB32:
00576         _expblur (pixels, width, height, 4, radius, 16, 7);
00577       break;
00578 
00579       case CAIRO_FORMAT_RGB24:
00580         _expblur (pixels, width, height, 3, radius, 16, 7);
00581       break;
00582 
00583       case CAIRO_FORMAT_A8:
00584         _expblur (pixels, width, height, 1, radius, 16, 7);
00585       break;
00586 
00587       default :
00588         // do nothing
00589       break;
00590     }
00591 
00592     // inform cairo we altered the surfaces contents
00593     cairo_surface_mark_dirty (surface);
00594 
00595     return true;
00596   }
00597 
00598   bool CairoGraphics::IntersectRectClipRegion(double x, double y, double w, double h)
00599   {
00600     if (w <= 0.0 || h <= 0.0) {
00601       return false;
00602     }
00603 
00604     cairo_antialias_t pre = cairo_get_antialias(_cr);
00605     cairo_set_antialias(_cr, CAIRO_ANTIALIAS_NONE);
00606     cairo_rectangle(_cr, x, y, w, h);
00607     cairo_clip(_cr);
00608     cairo_set_antialias(_cr, pre);
00609     return true;
00610   }
00611 
00612   bool CairoGraphics::IntersectGeneralClipRegion(std::list<Rect> &region)
00613   {
00614     bool do_clip = false;
00615     cairo_antialias_t pre = cairo_get_antialias(_cr);
00616     cairo_set_antialias(_cr, CAIRO_ANTIALIAS_NONE);
00617     
00618     std::list<Rect>::iterator it;
00619     for (it = region.begin(); it != region.end(); it++)
00620     {
00621       Rect rect = (*it);
00622 
00623       if (!rect.IsNull())
00624       {
00625         cairo_rectangle(_cr, rect.x, rect.y, rect.width, rect.height);
00626         do_clip = true;
00627       }
00628     }
00629 
00630     if (do_clip)
00631     {
00632       cairo_clip(_cr);
00633     }
00634 
00635     cairo_set_antialias(_cr, pre);
00636     return true;
00637   }
00638 }
00639 
 All Classes Namespaces Functions Variables Typedefs Enumerations Enumerator Friends