nux-1.14.0
|
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> ®ion) 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