void paint(Painter& painter) override { const auto r = screen_rect(); const auto& s = style(); Rect target_rect { r.pos, { r.width(), s.font.line_height() }}; const size_t visible_item_count = r.height() / s.font.line_height(); auto selected = recent.find(selected_key); if( selected == std::end(recent) ) { selected = std::begin(recent); } auto range = recent.range_around(selected, visible_item_count); for(auto p = range.first; p != range.second; p++) { const auto& entry = *p; const auto is_selected_key = (selected_key == entry.key()); draw(entry, target_rect, painter, s, (has_focus() && is_selected_key)); target_rect.pos.y += target_rect.height(); } painter.fill_rectangle( { target_rect.left(), target_rect.top(), target_rect.width(), r.bottom() - target_rect.top() }, style().background ); }
void Channel::paint(Painter& painter) { const auto r = screen_rect(); const int32_t db_min = -r.width(); const int32_t x_0 = 0; const int32_t x_max = std::max(x_0, max_db_ - db_min); const int32_t x_lim = r.width(); const Rect r0 { static_cast<ui::Coord>(r.left() + x_0), r.top(), static_cast<ui::Dim>(x_max - x_0), r.height() }; painter.fill_rectangle( r0, Color::blue() ); const Rect r1 { static_cast<ui::Coord>(r.left() + x_max), r.top(), 1, r.height() }; painter.fill_rectangle( r1, Color::white() ); const Rect r2 { static_cast<ui::Coord>(r.left() + x_max + 1), r.top(), static_cast<ui::Dim>(x_lim - (x_max + 1)), r.height() }; painter.fill_rectangle( r2, Color::black() ); }
void RSSI::paint(Painter& painter) { const auto r = screen_rect(); /* constexpr int32_t rssi_min = 0.# * 256 / 3.3; constexpr int32_t rssi_max = 2.5 * 256 / 3.3; // (23 - 194) / 2 */ /* TODO: Clip maximum */ constexpr int32_t raw_min = 23; const int32_t x_0 = 0; const int32_t x_min = std::max(x_0, (min_ - raw_min) / 2); const int32_t x_avg = std::max(x_min, (avg_ - raw_min) / 2); const int32_t x_max = std::max(x_avg + 1, (max_ - raw_min) / 2); const int32_t x_lim = r.width(); const Rect r0 { static_cast<ui::Coord>(r.left() + x_0), r.top(), static_cast<ui::Dim>(x_min - x_0), r.height() }; painter.fill_rectangle( r0, Color::blue() ); const Rect r1 { static_cast<ui::Coord>(r.left() + x_min), r.top(), static_cast<ui::Dim>(x_avg - x_min), r.height() }; painter.fill_rectangle( r1, Color::red() ); const Rect r2 { static_cast<ui::Coord>(r.left() + x_avg), r.top(), 1, r.height() }; painter.fill_rectangle( r2, Color::white() ); const Rect r3 { static_cast<ui::Coord>(r.left() + x_avg + 1), r.top(), static_cast<ui::Dim>(x_max - (x_avg + 1)), r.height() }; painter.fill_rectangle( r3, Color::red() ); const Rect r4 { static_cast<ui::Coord>(r.left() + x_max), r.top(), static_cast<ui::Dim>(x_lim - x_max), r.height() }; painter.fill_rectangle( r4, Color::black() ); }
void Console::clear() { display.fill_rectangle( screen_rect(), Color::black() ); pos = { 0, 0 }; display.scroll_set_position(0); }
StillImage VideoEngine::CaptureScreen() throw(Exception) { // Static variable used to make sure the capture has a unique name in the texture image map static uint32 capture_id = 0; StillImage screen_image; // Retrieve width/height of the viewport. viewport_dimensions[2] is the width, [3] is the height GLint viewport_dimensions[4]; glGetIntegerv(GL_VIEWPORT, viewport_dimensions); screen_image.SetDimensions((float)viewport_dimensions[2], (float)viewport_dimensions[3]); // Set up the screen rectangle to copy ScreenRect screen_rect(0, viewport_dimensions[3], viewport_dimensions[2], viewport_dimensions[3]); // Create a new ImageTexture with a unique filename for this newly captured screen ImageTexture *new_image = new ImageTexture("capture_screen" + NumberToString(capture_id), "<T>", viewport_dimensions[2], viewport_dimensions[3]); new_image->AddReference(); // Create a texture sheet of an appropriate size that can retain the capture TexSheet *temp_sheet = TextureManager->_CreateTexSheet(RoundUpPow2(viewport_dimensions[2]), RoundUpPow2(viewport_dimensions[3]), VIDEO_TEXSHEET_ANY, false); VariableTexSheet *sheet = dynamic_cast<VariableTexSheet *>(temp_sheet); // Ensure that texture sheet creation succeeded, insert the texture image into the sheet, and copy the screen into the sheet if(sheet == NULL) { delete new_image; throw Exception("could not create texture sheet to store captured screen", __FILE__, __LINE__, __FUNCTION__); screen_image.Clear(); return screen_image; } if(sheet->InsertTexture(new_image) == false) { TextureManager->_RemoveSheet(sheet); delete new_image; throw Exception("could not insert captured screen image into texture sheet", __FILE__, __LINE__, __FUNCTION__); screen_image.Clear(); return screen_image; } if(sheet->CopyScreenRect(0, 0, screen_rect) == false) { TextureManager->_RemoveSheet(sheet); delete new_image; throw Exception("call to TexSheet::CopyScreenRect() failed", __FILE__, __LINE__, __FUNCTION__); screen_image.Clear(); return screen_image; } // Store the image element to the saved image (with a flipped y axis) screen_image._image_texture = new_image; screen_image._texture = new_image; // Vertically flip the texture image by swapping the v coordinates, since OpenGL returns the image upside down in the CopyScreenRect call float temp = new_image->v1; new_image->v1 = new_image->v2; new_image->v2 = temp; ++capture_id; return screen_image; }
void Text::paint(Painter& painter) { const auto rect = screen_rect(); const auto s = style(); painter.fill_rectangle(rect, s.background); painter.draw_string( rect.location(), s, text ); }
bool Button::on_touch(const TouchEvent event) { switch(event.type) { case TouchEvent::Type::Start: set_highlighted(true); set_dirty(); return true; case TouchEvent::Type::End: set_highlighted(false); set_dirty(); if( on_select ) { on_select(*this); } return true; default: return false; } #if 0 switch(event.type) { case TouchEvent::Type::Start: flags.highlighted = true; set_dirty(); return true; case TouchEvent::Type::Move: { const bool new_highlighted = screen_rect().contains(event.point); if( flags.highlighted != new_highlighted ) { flags.highlighted = new_highlighted; set_dirty(); } } return true; case TouchEvent::Type::End: if( flags.highlighted ) { flags.highlighted = false; set_dirty(); if( on_select ) { on_select(*this); } } return true; default: return false; } #endif }
void TemperatureWidget::paint(Painter& painter) { const auto logger = portapack::temperature_logger; const auto rect = screen_rect(); const Color color_background { 0, 0, 64 }; const Color color_foreground = Color::green(); const Color color_reticle { 128, 128, 128 }; const auto graph_width = static_cast<int>(logger.capacity()) * bar_width; const Rect graph_rect { rect.left() + (rect.width() - graph_width) / 2, rect.top() + 8, graph_width, rect.height() }; const Rect frame_rect { graph_rect.left() - 1, graph_rect.top() - 1, graph_rect.width() + 2, graph_rect.height() + 2 }; painter.draw_rectangle(frame_rect, color_reticle); painter.fill_rectangle(graph_rect, color_background); const auto history = logger.history(); for(size_t i=0; i<history.size(); i++) { const Coord x = graph_rect.right() - (history.size() - i) * bar_width; const auto sample = history[i]; const auto temp = temperature(sample); const auto y = screen_y(temp, graph_rect); const Dim bar_height = graph_rect.bottom() - y; painter.fill_rectangle({ x, y, bar_width, bar_height }, color_foreground); } if( !history.empty() ) { const auto sample = history.back(); const auto temp = temperature(sample); const auto last_y = screen_y(temp, graph_rect); const Coord x = graph_rect.right() + 8; const Coord y = last_y - 8; painter.draw_string({ x, y }, style(), temperature_str(temp)); } const auto display_temp_max = display_temp_min + (graph_rect.height() / display_temp_scale); for(auto temp=display_temp_min; temp<=display_temp_max; temp+=10) { const int32_t tick_length = 6; const auto tick_x = graph_rect.left() - tick_length; const auto tick_y = screen_y(temp, graph_rect); painter.fill_rectangle({ tick_x, tick_y, tick_length, 1 }, color_reticle); const auto text_x = graph_rect.left() - temp_len * 8 - 8; const auto text_y = tick_y - 8; painter.draw_string({ text_x, text_y }, style(), temperature_str(temp)); } }
void Widget::set_parent(Widget* const widget) { if( widget == parent_ ) { return; } if( parent_ && !widget ) { // We have a parent, but are losing it. Update visible status. dirty_overlapping_children_in_rect(screen_rect()); visible(false); } parent_ = widget; set_dirty(); }
void Console::crlf() { const Style& s = style(); const auto sr = screen_rect(); const auto line_height = s.font.line_height(); pos.x = 0; pos.y += line_height; const int32_t y_excess = pos.y + line_height - sr.height(); if( y_excess > 0 ) { display.scroll(-y_excess); pos.y -= y_excess; const Rect dirty { sr.left(), display.scroll_area_y(pos.y), sr.width(), line_height }; display.fill_rectangle(dirty, s.background); } }
void Audio::paint(Painter& painter) { const auto r = screen_rect(); constexpr int db_min = -96; constexpr int db_max = 0; constexpr int db_delta = db_max - db_min; const range_t<int> x_rms_range { 0, r.width() - 1 }; const auto x_rms = x_rms_range.clip((rms_db_ - db_min) * r.width() / db_delta); const range_t<int> x_max_range { x_rms + 1, r.width() }; const auto x_max = x_max_range.clip((max_db_ - db_min) * r.width() / db_delta); const Rect r0 { static_cast<ui::Coord>(r.left()), r.top(), static_cast<ui::Dim>(x_rms), r.height() }; painter.fill_rectangle( r0, Color::green() ); const Rect r1 { static_cast<ui::Coord>(r.left() + x_rms), r.top(), 1, r.height() }; painter.fill_rectangle( r1, Color::black() ); const Rect r2 { static_cast<ui::Coord>(r.left() + x_rms + 1), r.top(), static_cast<ui::Dim>(x_max - (x_rms + 1)), r.height() }; painter.fill_rectangle( r2, Color::red() ); const Rect r3 { static_cast<ui::Coord>(r.left() + x_max), r.top(), static_cast<ui::Dim>(r.width() - x_max), r.height() }; painter.fill_rectangle( r3, Color::black() ); }
void Console::write(const std::string message) { const Style& s = style(); const Font& font = s.font; const auto rect = screen_rect(); for(const auto c : message) { const auto glyph = font.glyph(c); const auto advance = glyph.advance(); if( (pos.x + advance.x) > rect.width() ) { crlf(); } const Point pos_glyph { static_cast<Coord>(rect.pos.x + pos.x), display.scroll_area_y(pos.y) }; display.draw_glyph(pos_glyph, glyph, s.foreground, s.background); pos.x += advance.x; } }
void MenuItemView::paint(Painter& painter) { const auto r = screen_rect(); const auto paint_style = (highlighted() && parent()->has_focus()) ? style().invert() : style(); const auto font_height = paint_style.font.line_height(); painter.fill_rectangle( r, paint_style.background ); painter.draw_string( { r.left() + 8, r.top() + (r.height() - font_height) / 2 }, paint_style, item.text ); }
void Button::paint(Painter& painter) { const auto r = screen_rect(); const auto paint_style = (has_focus() || highlighted()) ? style().invert() : style(); painter.draw_rectangle(r, style().foreground); painter.fill_rectangle( { r.left() + 1, r.top() + 1, r.width() - 2, r.height() - 2 }, paint_style.background ); const auto label_r = paint_style.font.size_of(text_); painter.draw_string( { r.left() + (r.width() - label_r.width()) / 2, r.top() + (r.height() - label_r.height()) / 2 }, paint_style, text_ ); }
void AISRecentEntryDetailView::paint(Painter& painter) { View::paint(painter); const auto s = style(); const auto rect = screen_rect(); auto field_rect = Rect { rect.left(), rect.top() + 16, rect.width(), 16 }; field_rect = draw_field(painter, field_rect, s, "MMSI", ais::format::mmsi(entry_.mmsi)); field_rect = draw_field(painter, field_rect, s, "Name", entry_.name); field_rect = draw_field(painter, field_rect, s, "Call", entry_.call_sign); field_rect = draw_field(painter, field_rect, s, "Dest", entry_.destination); field_rect = draw_field(painter, field_rect, s, "Last", to_string_datetime(entry_.last_position.timestamp)); field_rect = draw_field(painter, field_rect, s, "Pos ", ais::format::latlon(entry_.last_position.latitude, entry_.last_position.longitude)); field_rect = draw_field(painter, field_rect, s, "Stat", ais::format::navigational_status(entry_.navigational_status)); field_rect = draw_field(painter, field_rect, s, "RoT ", ais::format::rate_of_turn(entry_.last_position.rate_of_turn)); field_rect = draw_field(painter, field_rect, s, "SoG ", ais::format::speed_over_ground(entry_.last_position.speed_over_ground)); field_rect = draw_field(painter, field_rect, s, "CoG ", ais::format::course_over_ground(entry_.last_position.course_over_ground)); field_rect = draw_field(painter, field_rect, s, "Head", ais::format::true_heading(entry_.last_position.true_heading)); field_rect = draw_field(painter, field_rect, s, "Rx #", to_string_dec_uint(entry_.received_count)); }
AboutWindow::AboutWindow () : BWindow (BRect (0, 0, 300, 200), "About", B_MODAL_WINDOW, B_NOT_ZOOMABLE | B_NOT_MINIMIZABLE | B_NOT_RESIZABLE) { PRINT (("AboutWindow::AboutWindow ()\n")); SetFeel (B_MODAL_APP_WINDOW_FEEL); SetLook (B_MODAL_WINDOW_LOOK); /* Create the BBitmap objects and set its data with error checking */ BBitmap *appIcon = new BBitmap (BRect (0, 0, kLargeIconWidth - 1, kLargeIconHeight - 1), B_COLOR_8_BIT); appIcon->SetBits (iconBits, 32 * 32 * 8, 0, B_COLOR_8_BIT); BBitmap *bmp = BTranslationUtils::GetBitmap ('PNG ', "Image:AboutTitle"); if (bmp == NULL) { BAlert *err = new BAlert ("Error", "An error was encountered while " "trying to load the resource element \"Image:AboutTitle\"\n", "Hmm..", NULL, NULL, B_WIDTH_AS_USUAL, B_OFFSET_SPACING, B_STOP_ALERT); err->Go(); Hide(); Quit(); QuitRequested(); return; } /* Yet another annoying control rendering section :( */ BRect bounds (Bounds()); backView = new BView (bounds.InsetBySelf (1, 1), "AboutWindow:BackView", B_FOLLOW_ALL_SIDES, B_WILL_DRAW); AddChild (backView); iconView = new BView (BRect (1.5 * DialogMargin + 3, 1.5 * DialogMargin, 1.5 * DialogMargin + kLargeIconWidth - 1 + 3, 1.5 * DialogMargin + kLargeIconWidth - 1), "AboutWindow:IconView", B_FOLLOW_LEFT, B_WILL_DRAW); backView->AddChild (iconView); iconView->SetViewBitmap (appIcon); float left = DialogMargin + kLargeIconWidth + 1.5 * ButtonSpacing - 3; float top = DialogMargin / 2.0; titleView = new BView (BRect (left, top, 214 + left, 58 + top), "AboutWindow:TitleView", B_FOLLOW_LEFT, B_WILL_DRAW); backView->AddChild (titleView); titleView->SetViewBitmap (bmp); lineView = new BView (BRect (0, titleView->Frame().bottom + 3, bounds.right, titleView->Frame().bottom + 3), "AboutWindow:LineView", B_FOLLOW_LEFT, B_WILL_DRAW); lineView->SetViewColor (128, 128, 128); backView->AddChild (lineView); textView = new MarqueeView (BRect (2, lineView->Frame().bottom + ButtonSpacing / 2 + 2, bounds.right - DialogMargin - 1, bounds.bottom - 2 - ButtonSpacing / 2), "AboutWindow:CreditsView", BRect (0, 0, bounds.right - DialogMargin, 0), B_FOLLOW_LEFT, B_WILL_DRAW); backView->AddChild (textView); textView->SetStylable (true); textView->MakeSelectable (false); textView->MakeEditable (false); textView->SetAlignment (B_ALIGN_CENTER); textView->SetViewColor (BePureWhite); backView->SetViewColor (BePureWhite); textView->SetFontAndColor (be_plain_font, B_FONT_ALL, &BeJetBlack); /* Calculate no of '\n's to leave to make the text go to the bottom, calculate the no. of lines */ font_height fntHt; textView->GetFontHeight (&fntHt); int32 noOfLines = (int32)(textView->Frame().Height() / fntHt.ascent) - 1; for (int32 i = 1; i < (int32)noOfLines; i++) lineFeeds << "\n"; creditsText = "Freeware, Version " AppVersion "\n" "Copyright " B_UTF8_COPYRIGHT " 2002 Ramshankar\n\n\n" CODING "\nRamshankar\n([email protected])\n\n* * *\n\n\n\n\n\n\n\n\n" THANKS_TO "\n\n" BUBBLEHELP "\nMarco Nelissen\n\n" BESHARE "\nJeremy Friesner\n\n" "Thank you all for your\n" "contributions with the code...\n\n* * *\n\n\n\n\n\n\n\n\n" "Also thanks to\n\n" "John Trujillo\nSebastian Benner\nM Floro\n\n" "for your support and contributions...\n\n* * *\n\n\n\n\n\n\n\n\n" "A special round of applause\n" "to BeShare members (in no\n" "particular order) :\n\n" "lillo\nshatty\nProcton\n" "Bryan\nPahtz\nmmu_man\nBeMikko\nNeil\nskiBUM\nand " "others\n\n" "for being so good... :)\n\n* * *\n\n\n\n\n\n\n\n\n" LEGAL_MUMBO_JUMBO "\n\n" "This program is distributed under\n" "its own license, and the gist of\n" "the license is attached to each\n" "source file of this program\n\n" "For third party code, the license's\n" "terms and conditions are explicitly\n" "stated and the author disclaimed of\n" "any and all liabilities\n\n" "For the full license read the\n" "file \"License.txt\" and for\n" "information on how to use this\n" "program read \"Readme.txt\"\n\n* * *\n\n\n\n\n\n\n\n\n" DISCLAIMER "\n\n" "Because the software is licensed\n" "free of charge, there is no warranty\n" "for the software. The copyright\n" "holders and/or other parties provide\n" "the software \"AS IS\" without warranty\n" "of any kind, either expressed or\n" "implied, including, but not limited to,\n" "the implied warranties of merchantability\n" "and fitness for a particular purpose.\n" "The entire risk as to the quality and\n" "performance of the software is with you.\n" "Should the software prove defective, you\n" "assume the cost of all necessary\n" "servicing, repair or correction.\n\n" "In no event will the copyright holder,\n" "or any other party who may modify and/or\n" "redistribute the software as permitted\n" "above, be liable to you for damages,\n" "including any general, special, incidental\n" "or consequential damages arising out of\n" "the use or inability to use the software\n" "(including but not limited to the loss of\n" "data or data being rendered inaccurate or\n" "losses sustained by you or third parties\n" "or a failure of the software to operate\n" "with any other programs), even if such\n" "holder or other party has been advised\n" "of the possibility of such damages.\n\n\n\n\n\n\n\n\n" FINAL_THANKS "\n\n" "Be, Inc., for making this OS\n" "in the first place\n\n" "OpenBeOS for their efforts to\n" "keep BeOS alive\n\n" "BeBits.com, BeGroovy.com, BeZip.de and\n" "other BeOS related sites for their\n" "continued enthusiasm and effort!\n\n" "BeOS programmers, designers, artists for\n" "their contributions to the OS' growth\n\n" "and a big applause goes to the Be\n" "community (it includes me too :)\n\n* * *\n\n\n\n\n\n\n\n\n" "OK... you can close this window now :)\n\n\n\n\n\n"; textView->SetText (lineFeeds.String()); textView->Insert (lineFeeds.Length(), creditsText, strlen(creditsText)); int32 nSubHeadings = 4; BString subHeadings[] = { CODING, // 0 BUBBLEHELP, // 2 BESHARE, // 3 DISCLAIMER // 4 }; int32 nMainHeadings = 3; BString mainHeadings[] = { THANKS_TO, // 0 LEGAL_MUMBO_JUMBO, // 1 FINAL_THANKS // 2 }; /* Search and color sub headings */ BString temp = textView->Text(); int32 strt; for (int32 i = 0; i < nSubHeadings; i++) { if ((strt = temp.FindFirst (subHeadings[i].String())) != B_ERROR) { textView->SetFontAndColor (strt, strt + strlen(subHeadings[i].String()), be_plain_font, B_FONT_ALL, &TreeLabelColor); } } /* Search and color main headings */ for (int32 i = 0; i < nMainHeadings; i++) { if ((strt = temp.FindFirst (mainHeadings[i].String())) != B_ERROR) { textView->SetFontAndColor (strt, strt + strlen(mainHeadings[i].String()), be_plain_font, B_FONT_ALL, &(rgb_color){0, 0, 200}); } } /* Center window on-screen */ BRect screen_rect (BScreen().Frame()); MoveTo (screen_rect.Width() / 2 - Frame().Width() / 2, screen_rect.Height() / 2 - Frame().Height() / 2); /* Delete unwanted stuff */ delete appIcon; delete bmp; /* Spawn & resume the scroller thread now */ PRINT ((" >> spawning_thread: Magic_Scroll\n")); thread_id tid = spawn_thread (ScrollIt, "Magic_Scroll", B_NORMAL_PRIORITY, (void*)this); resume_thread (tid); }
bool CCommandSampleZoomRotateWindow::DollyWindow( const CRhinoViewport& vport, CRect pick_rect, ON_3dPoint& target_point, ON_Viewport& vp_out ) { const ON_Viewport& vp_in = vport.VP(); vp_out = vp_in; // screen port values int sleft, sright, stop, sbottom; if( !vp_in.GetScreenPort(&sleft, &sright, &sbottom, &stop) ) return false; // frustum values double fleft, fright, ftop, fbottom, fnear, ffar; if( !vp_in.GetFrustum(&fleft, &fright, &fbottom, &ftop, &fnear, &ffar) ) return false; // camera coordinate system ON_3dPoint cam_loc = vp_in.CameraLocation(); ON_3dVector cam_z = vp_in.CameraZ(); // capture Depth Buffer CRect screen_rect( sleft, stop, sright, sbottom ); // user-specified rectangle CRect zoom_rect; zoom_rect.IntersectRect( &pick_rect, screen_rect ); CRhinoZBuffer zbuffer( vport ); zbuffer.ShowIsocurves( true ); zbuffer.ShowMeshWires( true ); zbuffer.ShowCurves( true ); zbuffer.ShowPoints( true ); zbuffer.ShowText( true ); zbuffer.ShowAnnotations( false ); bool bSeeThrough = ( vport.XrayShade() || vport.GhostedShade() ); if( bSeeThrough ) zbuffer.EnableShading( false ); bool rc = zbuffer.Capture( zoom_rect ); if( rc && bSeeThrough && 0 == zbuffer.HitCount() ) { zbuffer.EnableShading( true ); zbuffer.ShowIsocurves( false ); zbuffer.ShowMeshWires( false ); zbuffer.ShowCurves( false ); zbuffer.ShowPoints( false ); zbuffer.ShowText( false ); rc = zbuffer.Capture( zoom_rect ); } ON_Xform s2c; if( rc ) rc = zbuffer.VP().GetXform( ON::screen_cs, ON::camera_cs, s2c ); if( rc ) { // dolly sideways so zoom rectangle is centered ON_3dPoint near_rect[4]; vp_in.GetNearRect( near_rect[0], near_rect[1], near_rect[2], near_rect[3] ); double port_dx = fabs( double(sright - sleft) ); double port_dy = fabs( double(stop - sbottom) ); ON_Interval lr( sleft, sright); double nx = lr.NormalizedParameterAt( 0.5 * (pick_rect.left + pick_rect.right) ); ON_Interval bt( sbottom, stop); double ny = bt.NormalizedParameterAt( 0.5 * (pick_rect.bottom + pick_rect.top) ); ON_3dPoint zoom_center = (1-nx) * (1-ny) * near_rect[0] + nx * (1-ny) * near_rect[1] + (1-nx) * ny * near_rect[2] + nx *ny * near_rect[3]; ON_3dVector dolly_vec = zoom_center - cam_loc; // dolly perpendicular to cam_z dolly_vec -= dolly_vec * cam_z * cam_z; vp_out.DollyCamera( dolly_vec ); double pick_rect_dx = fabs(double (pick_rect.right - pick_rect.left)); double pick_rect_dy = fabs(double(pick_rect.top - pick_rect.bottom)); // expand pick_rect to have the aspect ratio of the viewport double d = 1.0; if( pick_rect_dx/port_dx < pick_rect_dy/port_dy) d = pick_rect_dy / port_dy; else d = pick_rect_dx / port_dx ; fleft *= d; fright *= d; fbottom *= d; ftop *=d ; vp_out.SetFrustum( fleft, fright, fbottom, ftop, fnear, ffar ); // target point on plane perpendicular to cam_z cam_loc = vp_out.CameraLocation(); target_point = cam_loc + (target_point - cam_loc) *cam_z * cam_z; } return rc; }
S32 LLFontGL::render(const LLWString &wstr, const S32 begin_offset, const F32 x, const F32 y, const LLColor4 &color, const HAlign halign, const VAlign valign, U8 style, const S32 max_chars, S32 max_pixels, F32* right_x, BOOL use_embedded, BOOL use_ellipses) const { if(!sDisplayFont) //do not display texts { return wstr.length() ; } LLGLEnable tex(GL_TEXTURE_2D); if (wstr.empty()) { return 0; } S32 scaled_max_pixels = max_pixels == S32_MAX ? S32_MAX : llceil((F32)max_pixels * sScaleX); // HACK for better bolding if (style & BOLD) { if (this == LLFontGL::sSansSerif) { return LLFontGL::sSansSerifBold->render( wstr, begin_offset, x, y, color, halign, valign, (style & ~BOLD), max_chars, max_pixels, right_x, use_embedded); } } F32 drop_shadow_strength = 0.f; if (style & (DROP_SHADOW | DROP_SHADOW_SOFT)) { F32 luminance; color.calcHSL(NULL, NULL, &luminance); drop_shadow_strength = clamp_rescale(luminance, 0.35f, 0.6f, 0.f, 1.f); if (luminance < 0.35f) { style = style & ~(DROP_SHADOW | DROP_SHADOW_SOFT); } } gGL.pushMatrix(); glLoadIdentity(); gGL.translatef(floorf(sCurOrigin.mX*sScaleX), floorf(sCurOrigin.mY*sScaleY), sCurOrigin.mZ); //glScalef(sScaleX, sScaleY, 1.0f); // avoid half pixels // RN: if we're going to this trouble, might as well snap to nearest pixel all the time // but the plan is to get rid of this so that fonts "just work" //F32 half_pixel_distance = llabs(fmodf(sCurOrigin.mX * sScaleX, 1.f) - 0.5f); //if (half_pixel_distance < PIXEL_BORDER_THRESHOLD) //{ gGL.translatef(PIXEL_CORRECTION_DISTANCE*sScaleX, 0.f, 0.f); //} // this code would just snap to pixel grid, although it seems to introduce more jitter //F32 pixel_offset_x = llround(sCurOrigin.mX * sScaleX) - (sCurOrigin.mX * sScaleX); //F32 pixel_offset_y = llround(sCurOrigin.mY * sScaleY) - (sCurOrigin.mY * sScaleY); //gGL.translatef(-pixel_offset_x, -pixel_offset_y, 0.f); // scale back to native pixel size //glScalef(1.f / sScaleX, 1.f / sScaleY, 1.f); //glScaled(1.0 / (F64) sScaleX, 1.0 / (F64) sScaleY, 1.0f); LLFastTimer t(LLFastTimer::FTM_RENDER_FONTS); gGL.color4fv( color.mV ); S32 chars_drawn = 0; S32 i; S32 length; if (-1 == max_chars) { length = (S32)wstr.length() - begin_offset; } else { length = llmin((S32)wstr.length() - begin_offset, max_chars ); } F32 cur_x, cur_y, cur_render_x, cur_render_y; // Bind the font texture mImageGLp->bind(0); // Not guaranteed to be set correctly gGL.setSceneBlendType(LLRender::BT_ALPHA); cur_x = ((F32)x * sScaleX); cur_y = ((F32)y * sScaleY); // Offset y by vertical alignment. switch (valign) { case TOP: cur_y -= mAscender; break; case BOTTOM: cur_y += mDescender; break; case VCENTER: cur_y -= ((mAscender - mDescender)/2.f); break; case BASELINE: // Baseline, do nothing. break; default: break; } switch (halign) { case LEFT: break; case RIGHT: cur_x -= llmin(scaled_max_pixels, llround(getWidthF32(wstr.c_str(), 0, length) * sScaleX)); break; case HCENTER: cur_x -= llmin(scaled_max_pixels, llround(getWidthF32(wstr.c_str(), 0, length) * sScaleX)) / 2; break; default: break; } // Round properly. //cur_render_y = (F32)llfloor(cur_y/sScaleY + 0.5f)*sScaleY; //cur_render_x = (F32)llfloor(cur_x/sScaleX + 0.5f)*sScaleX; cur_render_y = cur_y; cur_render_x = cur_x; F32 start_x = cur_x; F32 inv_width = 1.f / mImageGLp->getWidth(); F32 inv_height = 1.f / mImageGLp->getHeight(); const S32 LAST_CHARACTER = LLFont::LAST_CHAR_FULL; BOOL draw_ellipses = FALSE; if (use_ellipses && halign == LEFT) { // check for too long of a string if (getWidthF32(wstr.c_str(), 0, max_chars) * sScaleX > scaled_max_pixels) { // use four dots for ellipsis width to generate padding const LLWString dots(utf8str_to_wstring(std::string("...."))); scaled_max_pixels = llmax(0, scaled_max_pixels - llround(getWidthF32(dots.c_str()))); draw_ellipses = TRUE; } } for (i = begin_offset; i < begin_offset + length; i++) { llwchar wch = wstr[i]; // Handle embedded characters first, if they're enabled. // Embedded characters are a hack for notecards const embedded_data_t* ext_data = use_embedded ? getEmbeddedCharData(wch) : NULL; if (ext_data) { LLImageGL* ext_image = ext_data->mImage; const LLWString& label = ext_data->mLabel; F32 ext_height = (F32)ext_image->getHeight() * sScaleY; F32 ext_width = (F32)ext_image->getWidth() * sScaleX; F32 ext_advance = (EXT_X_BEARING * sScaleX) + ext_width; if (!label.empty()) { ext_advance += (EXT_X_BEARING + gExtCharFont->getWidthF32( label.c_str() )) * sScaleX; } if (start_x + scaled_max_pixels < cur_x + ext_advance) { // Not enough room for this character. break; } ext_image->bind(); const F32 ext_x = cur_render_x + (EXT_X_BEARING * sScaleX); const F32 ext_y = cur_render_y + (EXT_Y_BEARING * sScaleY + mAscender - mLineHeight); LLRectf uv_rect(0.f, 1.f, 1.f, 0.f); LLRectf screen_rect(ext_x, ext_y + ext_height, ext_x + ext_width, ext_y); drawGlyph(screen_rect, uv_rect, LLColor4::white, style, drop_shadow_strength); if (!label.empty()) { gGL.pushMatrix(); //glLoadIdentity(); //gGL.translatef(sCurOrigin.mX, sCurOrigin.mY, 0.0f); //glScalef(sScaleX, sScaleY, 1.f); gExtCharFont->render(label, 0, /*llfloor*/((ext_x + (F32)ext_image->getWidth() + EXT_X_BEARING) / sScaleX), /*llfloor*/(cur_y / sScaleY), color, halign, BASELINE, NORMAL, S32_MAX, S32_MAX, NULL, TRUE ); gGL.popMatrix(); } gGL.color4fv(color.mV); chars_drawn++; cur_x += ext_advance; if (((i + 1) < length) && wstr[i+1]) { cur_x += EXT_KERNING * sScaleX; } cur_render_x = cur_x; // Bind the font texture mImageGLp->bind(); } else { if (!hasGlyph(wch)) { (const_cast<LLFontGL*>(this))->addChar(wch); } const LLFontGlyphInfo* fgi= getGlyphInfo(wch); if (!fgi) { llerrs << "Missing Glyph Info" << llendl; break; } if ((start_x + scaled_max_pixels) < (cur_x + fgi->mXBearing + fgi->mWidth)) { // Not enough room for this character. break; } // Draw the text at the appropriate location //Specify vertices and texture coordinates LLRectf uv_rect((fgi->mXBitmapOffset - PAD_AMT) * inv_width, (fgi->mYBitmapOffset + fgi->mHeight + PAD_AMT) * inv_height, (fgi->mXBitmapOffset + fgi->mWidth + PAD_AMT) * inv_width, (fgi->mYBitmapOffset - PAD_AMT) * inv_height); LLRectf screen_rect(cur_render_x + (F32)fgi->mXBearing - PAD_AMT, cur_render_y + (F32)fgi->mYBearing + PAD_AMT, cur_render_x + (F32)fgi->mXBearing + (F32)fgi->mWidth + PAD_AMT, cur_render_y + (F32)fgi->mYBearing - (F32)fgi->mHeight - PAD_AMT); drawGlyph(screen_rect, uv_rect, color, style, drop_shadow_strength); chars_drawn++; cur_x += fgi->mXAdvance; cur_y += fgi->mYAdvance; llwchar next_char = wstr[i+1]; if (next_char && (next_char < LAST_CHARACTER)) { // Kern this puppy. if (!hasGlyph(next_char)) { (const_cast<LLFontGL*>(this))->addChar(next_char); } cur_x += getXKerning(wch, next_char); } // Round after kerning. // Must do this to cur_x, not just to cur_render_x, otherwise you // will squish sub-pixel kerned characters too close together. // For example, "CCCCC" looks bad. cur_x = (F32)llfloor(cur_x + 0.5f); //cur_y = (F32)llfloor(cur_y + 0.5f); cur_render_x = cur_x; cur_render_y = cur_y; } } if (right_x) { *right_x = cur_x / sScaleX; } if (style & UNDERLINE) { LLGLSNoTexture no_texture; gGL.begin(LLVertexBuffer::LINES); gGL.vertex2f(start_x, cur_y - (mDescender)); gGL.vertex2f(cur_x, cur_y - (mDescender)); gGL.end(); } // *FIX: get this working in all alignment cases, etc. if (draw_ellipses) { // recursively render ellipses at end of string // we've already reserved enough room gGL.pushMatrix(); //glLoadIdentity(); //gGL.translatef(sCurOrigin.mX, sCurOrigin.mY, 0.0f); //glScalef(sScaleX, sScaleY, 1.f); renderUTF8(std::string("..."), 0, cur_x / sScaleX, (F32)y, color, LEFT, valign, style, S32_MAX, max_pixels, right_x, FALSE); gGL.popMatrix(); } gGL.popMatrix(); return chars_drawn; }
Point Widget::screen_pos() { return screen_rect().location(); }
S32 LLFontGL::render(const LLWString &wstr, S32 begin_offset, F32 x, F32 y, const LLColor4 &color, HAlign halign, VAlign valign, U8 style, ShadowType shadow, S32 max_chars, S32 max_pixels, F32* right_x, BOOL use_ellipses) const { LLFastTimer _(FTM_RENDER_FONTS); if(!sDisplayFont) //do not display texts { return wstr.length() ; } if (wstr.empty()) { return 0; } gGL.getTexUnit(0)->enable(LLTexUnit::TT_TEXTURE); S32 scaled_max_pixels = max_pixels == S32_MAX ? S32_MAX : llceil((F32)max_pixels * sScaleX); // determine which style flags need to be added programmatically by stripping off the // style bits that are drawn by the underlying Freetype font U8 style_to_add = (style | mFontDescriptor.getStyle()) & ~mFontFreetype->getStyle(); F32 drop_shadow_strength = 0.f; if (shadow != NO_SHADOW) { F32 luminance; color.calcHSL(NULL, NULL, &luminance); drop_shadow_strength = clamp_rescale(luminance, 0.35f, 0.6f, 0.f, 1.f); if (luminance < 0.35f) { shadow = NO_SHADOW; } } gGL.pushUIMatrix(); gGL.loadUIIdentity(); //gGL.translateUI(floorf(sCurOrigin.mX*sScaleX), floorf(sCurOrigin.mY*sScaleY), sCurOrigin.mZ); // this code snaps the text origin to a pixel grid to start with //F32 pixel_offset_x = llround((F32)sCurOrigin.mX) - (sCurOrigin.mX); //F32 pixel_offset_y = llround((F32)sCurOrigin.mY) - (sCurOrigin.mY); //gGL.translateUI(-pixel_offset_x, -pixel_offset_y, 0.f); LLVector2 origin(floorf(sCurOrigin.mX*sScaleX), floorf(sCurOrigin.mY*sScaleY)); // snap the text origin to a pixel grid to start with origin.mV[VX] -= llround((F32)sCurOrigin.mX) - (sCurOrigin.mX); origin.mV[VY] -= llround((F32)sCurOrigin.mY) - (sCurOrigin.mY); S32 chars_drawn = 0; S32 i; S32 length; if (-1 == max_chars) { length = (S32)wstr.length() - begin_offset; } else { length = llmin((S32)wstr.length() - begin_offset, max_chars ); } F32 cur_x, cur_y, cur_render_x, cur_render_y; // Not guaranteed to be set correctly gGL.setSceneBlendType(LLRender::BT_ALPHA); cur_x = ((F32)x * sScaleX) + origin.mV[VX]; cur_y = ((F32)y * sScaleY) + origin.mV[VY]; // Offset y by vertical alignment. switch (valign) { case TOP: cur_y -= mFontFreetype->getAscenderHeight(); break; case BOTTOM: cur_y += mFontFreetype->getDescenderHeight(); break; case VCENTER: cur_y -= (mFontFreetype->getAscenderHeight() - mFontFreetype->getDescenderHeight()) / 2.f; break; case BASELINE: // Baseline, do nothing. break; default: break; } switch (halign) { case LEFT: break; case RIGHT: cur_x -= llmin(scaled_max_pixels, llround(getWidthF32(wstr.c_str(), begin_offset, length) * sScaleX)); break; case HCENTER: cur_x -= llmin(scaled_max_pixels, llround(getWidthF32(wstr.c_str(), begin_offset, length) * sScaleX)) / 2; break; default: break; } cur_render_y = cur_y; cur_render_x = cur_x; F32 start_x = llround(cur_x); const LLFontBitmapCache* font_bitmap_cache = mFontFreetype->getFontBitmapCache(); F32 inv_width = 1.f / font_bitmap_cache->getBitmapWidth(); F32 inv_height = 1.f / font_bitmap_cache->getBitmapHeight(); const S32 LAST_CHARACTER = LLFontFreetype::LAST_CHAR_FULL; BOOL draw_ellipses = FALSE; if (use_ellipses) { // check for too long of a string S32 string_width = llround(getWidthF32(wstr.c_str(), begin_offset, max_chars) * sScaleX); if (string_width > scaled_max_pixels) { // use four dots for ellipsis width to generate padding const LLWString dots(utf8str_to_wstring(std::string("...."))); scaled_max_pixels = llmax(0, scaled_max_pixels - llround(getWidthF32(dots.c_str()))); draw_ellipses = TRUE; } } const LLFontGlyphInfo* next_glyph = NULL; const S32 GLYPH_BATCH_SIZE = 30; LLVector3 vertices[GLYPH_BATCH_SIZE * 4]; LLVector2 uvs[GLYPH_BATCH_SIZE * 4]; LLColor4U colors[GLYPH_BATCH_SIZE * 4]; LLColor4U text_color(color); S32 bitmap_num = -1; S32 glyph_count = 0; for (i = begin_offset; i < begin_offset + length; i++) { llwchar wch = wstr[i]; const LLFontGlyphInfo* fgi = next_glyph; next_glyph = NULL; if(!fgi) { fgi = mFontFreetype->getGlyphInfo(wch); } if (!fgi) { llerrs << "Missing Glyph Info" << llendl; break; } // Per-glyph bitmap texture. S32 next_bitmap_num = fgi->mBitmapNum; if (next_bitmap_num != bitmap_num) { bitmap_num = next_bitmap_num; LLImageGL *font_image = font_bitmap_cache->getImageGL(bitmap_num); gGL.getTexUnit(0)->bind(font_image); } if ((start_x + scaled_max_pixels) < (cur_x + fgi->mXBearing + fgi->mWidth)) { // Not enough room for this character. break; } // Draw the text at the appropriate location //Specify vertices and texture coordinates LLRectf uv_rect((fgi->mXBitmapOffset) * inv_width, (fgi->mYBitmapOffset + fgi->mHeight + PAD_UVY) * inv_height, (fgi->mXBitmapOffset + fgi->mWidth) * inv_width, (fgi->mYBitmapOffset - PAD_UVY) * inv_height); // snap glyph origin to whole screen pixel LLRectf screen_rect(llround(cur_render_x + (F32)fgi->mXBearing), llround(cur_render_y + (F32)fgi->mYBearing), llround(cur_render_x + (F32)fgi->mXBearing) + (F32)fgi->mWidth, llround(cur_render_y + (F32)fgi->mYBearing) - (F32)fgi->mHeight); if (glyph_count >= GLYPH_BATCH_SIZE) { gGL.begin(LLRender::QUADS); { gGL.vertexBatchPreTransformed(vertices, uvs, colors, glyph_count * 4); } gGL.end(); glyph_count = 0; } drawGlyph(glyph_count, vertices, uvs, colors, screen_rect, uv_rect, text_color, style_to_add, shadow, drop_shadow_strength); chars_drawn++; cur_x += fgi->mXAdvance; cur_y += fgi->mYAdvance; llwchar next_char = wstr[i+1]; if (next_char && (next_char < LAST_CHARACTER)) { // Kern this puppy. next_glyph = mFontFreetype->getGlyphInfo(next_char); cur_x += mFontFreetype->getXKerning(fgi, next_glyph); } // Round after kerning. // Must do this to cur_x, not just to cur_render_x, otherwise you // will squish sub-pixel kerned characters too close together. // For example, "CCCCC" looks bad. cur_x = (F32)llround(cur_x); //cur_y = (F32)llround(cur_y); cur_render_x = cur_x; cur_render_y = cur_y; } gGL.begin(LLRender::QUADS); { gGL.vertexBatchPreTransformed(vertices, uvs, colors, glyph_count * 4); } gGL.end(); if (right_x) { *right_x = (cur_x - origin.mV[VX]) / sScaleX; } //FIXME: add underline as glyph? if (style_to_add & UNDERLINE) { F32 descender = mFontFreetype->getDescenderHeight(); gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); gGL.begin(LLRender::LINES); gGL.vertex2f(start_x, cur_y - (descender)); gGL.vertex2f(cur_x, cur_y - (descender)); gGL.end(); } if (draw_ellipses) { // recursively render ellipses at end of string // we've already reserved enough room gGL.pushUIMatrix(); renderUTF8(std::string("..."), 0, (cur_x - origin.mV[VX]) / sScaleX, (F32)y, color, LEFT, valign, style_to_add, shadow, S32_MAX, max_pixels, right_x, FALSE); gGL.popUIMatrix(); } gGL.popUIMatrix(); return chars_drawn; }
S32 LLFontGL::render(const LLWString &wstr, S32 begin_offset, F32 x, F32 y, const LLColor4 &color, HAlign halign, VAlign valign, U8 style, ShadowType shadow, S32 max_chars, S32 max_pixels, F32* right_x, BOOL use_embedded, BOOL use_ellipses) const { LLFastTimer _(FTM_RENDER_FONTS); if(!sDisplayFont) //do not display texts { return wstr.length() ; } if (wstr.empty() || !max_pixels) { return 0; } if (max_chars == -1) max_chars = S32_MAX; const S32 max_index = llmin(llmax(max_chars, begin_offset + max_chars), S32(wstr.length())); if (max_index <= 0 || begin_offset >= max_index || max_pixels <= 0) return 0; gGL.getTexUnit(0)->enable(LLTexUnit::TT_TEXTURE); S32 scaled_max_pixels = max_pixels == S32_MAX ? S32_MAX : llceil((F32)max_pixels * sScaleX); // Strip off any style bits that are already accounted for by the font. style = style & (~getFontDesc().getStyle()); F32 drop_shadow_strength = 0.f; if (shadow != NO_SHADOW) { F32 luminance; color.calcHSL(NULL, NULL, &luminance); drop_shadow_strength = clamp_rescale(luminance, 0.35f, 0.6f, 0.f, 1.f); if (luminance < 0.35f) { shadow = NO_SHADOW; } } gGL.pushUIMatrix(); gGL.loadUIIdentity(); LLVector2 origin(floorf(sCurOrigin.mX*sScaleX), floorf(sCurOrigin.mY*sScaleY)); // Depth translation, so that floating text appears 'in-world' // and is correctly occluded. gGL.translatef(0.f,0.f,sCurDepth); S32 chars_drawn = 0; S32 i; S32 length = max_index - begin_offset; F32 cur_x, cur_y, cur_render_x, cur_render_y; // Not guaranteed to be set correctly gGL.setSceneBlendType(LLRender::BT_ALPHA); cur_x = ((F32)x * sScaleX) + origin.mV[VX]; cur_y = ((F32)y * sScaleY) + origin.mV[VY]; // Offset y by vertical alignment. // use unscaled font metrics here switch (valign) { case TOP: cur_y -= llceil(mFontFreetype->getAscenderHeight()); break; case BOTTOM: cur_y += llceil(mFontFreetype->getDescenderHeight()); break; case VCENTER: cur_y -= llceil((llceil(mFontFreetype->getAscenderHeight()) - llceil(mFontFreetype->getDescenderHeight())) / 2.f); break; case BASELINE: // Baseline, do nothing. break; default: break; } switch (halign) { case LEFT: break; case RIGHT: cur_x -= llmin(scaled_max_pixels, ll_round(getWidthF32(wstr.c_str(), begin_offset, length) * sScaleX)); break; case HCENTER: cur_x -= llmin(scaled_max_pixels, ll_round(getWidthF32(wstr.c_str(), begin_offset, length) * sScaleX)) / 2; break; default: break; } cur_render_y = cur_y; cur_render_x = cur_x; F32 start_x = (F32)ll_round(cur_x); const LLFontBitmapCache* font_bitmap_cache = mFontFreetype->getFontBitmapCache(); F32 inv_width = 1.f / font_bitmap_cache->getBitmapWidth(); F32 inv_height = 1.f / font_bitmap_cache->getBitmapHeight(); const S32 LAST_CHARACTER = LLFontFreetype::LAST_CHAR_FULL; BOOL draw_ellipses = FALSE; if (use_ellipses && halign == LEFT) { // check for too long of a string S32 string_width = ll_round(getWidthF32(wstr, begin_offset, max_chars) * sScaleX); if (string_width > scaled_max_pixels) { // use four dots for ellipsis width to generate padding const LLWString dots(utf8str_to_wstring(std::string("...."))); scaled_max_pixels = llmax(0, scaled_max_pixels - ll_round(getWidthF32(dots.c_str()))); draw_ellipses = TRUE; } } const LLFontGlyphInfo* next_glyph = NULL; const S32 GLYPH_BATCH_SIZE = 30; static LL_ALIGN_16(LLVector4a vertices[GLYPH_BATCH_SIZE * 4]); static LLVector2 uvs[GLYPH_BATCH_SIZE * 4]; static LLColor4U colors[GLYPH_BATCH_SIZE * 4]; LLColor4U text_color(color); S32 bitmap_num = -1; S32 glyph_count = 0; for (i = begin_offset; i < begin_offset + length; i++) { llwchar wch = wstr[i]; // Handle embedded characters first, if they're enabled. // Embedded characters are a hack for notecards const embedded_data_t* ext_data = use_embedded ? getEmbeddedCharData(wch) : NULL; if (ext_data) { LLImageGL* ext_image = ext_data->mImage; const LLWString& label = ext_data->mLabel; F32 ext_height = (F32)ext_image->getHeight() * sScaleY; F32 ext_width = (F32)ext_image->getWidth() * sScaleX; F32 ext_advance = (EXT_X_BEARING * sScaleX) + ext_width; if (!label.empty()) { ext_advance += (EXT_X_BEARING + getFontExtChar()->getWidthF32( label.c_str() )) * sScaleX; } if (start_x + scaled_max_pixels < cur_x + ext_advance) { // Not enough room for this character. break; } gGL.getTexUnit(0)->bind(ext_image); // snap origin to whole screen pixel const F32 ext_x = (F32)ll_round(cur_render_x + (EXT_X_BEARING * sScaleX)); const F32 ext_y = (F32)ll_round(cur_render_y + (EXT_Y_BEARING * sScaleY + mFontFreetype->getAscenderHeight() - mFontFreetype->getLineHeight())); LLRectf uv_rect(0.f, 1.f, 1.f, 0.f); LLRectf screen_rect(ext_x, ext_y + ext_height, ext_x + ext_width, ext_y); if (glyph_count > 0) { gGL.begin(LLRender::QUADS); { gGL.vertexBatchPreTransformed(vertices, uvs, colors, glyph_count * 4); } gGL.end(); glyph_count = 0; } renderQuad(vertices, uvs, colors, screen_rect, uv_rect, LLColor4U::white, 0); //No batching here. It will never happen. gGL.begin(LLRender::QUADS); { gGL.vertexBatchPreTransformed(vertices, uvs, colors, 4); } gGL.end(); if (!label.empty()) { gGL.pushMatrix(); getFontExtChar()->render(label, 0, /*llfloor*/(ext_x / sScaleX) + ext_image->getWidth() + EXT_X_BEARING - sCurOrigin.mX, /*llfloor*/(cur_render_y / sScaleY) - sCurOrigin.mY, color, halign, BASELINE, UNDERLINE, NO_SHADOW, S32_MAX, S32_MAX, NULL, TRUE ); gGL.popMatrix(); } chars_drawn++; cur_x += ext_advance; if (((i + 1) < length) && wstr[i+1]) { cur_x += EXT_KERNING * sScaleX; } cur_render_x = cur_x; } else { const LLFontGlyphInfo* fgi = next_glyph; next_glyph = NULL; if(!fgi) { fgi = mFontFreetype->getGlyphInfo(wch); } if (!fgi) { LL_ERRS() << "Missing Glyph Info" << LL_ENDL; break; } // Per-glyph bitmap texture. S32 next_bitmap_num = fgi->mBitmapNum; if (next_bitmap_num != bitmap_num) { // Actually draw the queued glyphs before switching their texture; // otherwise the queued glyphs will be taken from wrong textures. if (glyph_count > 0) { gGL.begin(LLRender::QUADS); { gGL.vertexBatchPreTransformed(vertices, uvs, colors, glyph_count * 4); } gGL.end(); glyph_count = 0; } bitmap_num = next_bitmap_num; LLImageGL *font_image = font_bitmap_cache->getImageGL(bitmap_num); gGL.getTexUnit(0)->bind(font_image); } if ((start_x + scaled_max_pixels) < (cur_x + fgi->mXBearing + fgi->mWidth)) { // Not enough room for this character. break; } // Draw the text at the appropriate location //Specify vertices and texture coordinates LLRectf uv_rect((fgi->mXBitmapOffset) * inv_width, (fgi->mYBitmapOffset + fgi->mHeight + PAD_UVY) * inv_height, (fgi->mXBitmapOffset + fgi->mWidth) * inv_width, (fgi->mYBitmapOffset - PAD_UVY) * inv_height); // snap glyph origin to whole screen pixel LLRectf screen_rect((F32)ll_round(cur_render_x + (F32)fgi->mXBearing), (F32)ll_round(cur_render_y + (F32)fgi->mYBearing), (F32)ll_round(cur_render_x + (F32)fgi->mXBearing) + (F32)fgi->mWidth, (F32)ll_round(cur_render_y + (F32)fgi->mYBearing) - (F32)fgi->mHeight); if (glyph_count >= GLYPH_BATCH_SIZE) { gGL.begin(LLRender::QUADS); { gGL.vertexBatchPreTransformed(vertices, uvs, colors, glyph_count * 4); } gGL.end(); glyph_count = 0; } drawGlyph(glyph_count, vertices, uvs, colors, screen_rect, uv_rect, text_color, style, shadow, drop_shadow_strength); chars_drawn++; cur_x += fgi->mXAdvance; cur_y += fgi->mYAdvance; llwchar next_char = wstr[i+1]; if (next_char && (next_char < LAST_CHARACTER)) { // Kern this puppy. next_glyph = mFontFreetype->getGlyphInfo(next_char); cur_x += mFontFreetype->getXKerning(fgi, next_glyph); } // Round after kerning. // Must do this to cur_x, not just to cur_render_x, otherwise you // will squish sub-pixel kerned characters too close together. // For example, "CCCCC" looks bad. cur_x = (F32)ll_round(cur_x); //cur_y = (F32)ll_round(cur_y); cur_render_x = cur_x; cur_render_y = cur_y; } } if(glyph_count) { gGL.begin(LLRender::QUADS); { gGL.vertexBatchPreTransformed(vertices, uvs, colors, glyph_count * 4); } gGL.end(); } if (right_x) { *right_x = (cur_x - origin.mV[VX]) / sScaleX; } if (style & UNDERLINE) { F32 descender = (F32)llfloor(mFontFreetype->getDescenderHeight()); gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); gGL.begin(LLRender::LINES); gGL.vertex2f(start_x, cur_y - descender); gGL.vertex2f(cur_x, cur_y - descender); gGL.end(); } if (draw_ellipses) { // recursively render ellipses at end of string // we've already reserved enough room gGL.pushUIMatrix(); renderUTF8(std::string("..."), 0, (cur_x - origin.mV[VX]) / sScaleX, (F32)y, color, LEFT, valign, style, shadow, S32_MAX, max_pixels, right_x, FALSE); gGL.popUIMatrix(); } gGL.popUIMatrix(); return chars_drawn; }
void Rectangle::paint(Painter& painter) { painter.fill_rectangle( screen_rect(), color ); }
void View::paint(Painter& painter) { painter.fill_rectangle( screen_rect(), style().background ); }
Matrix32 Camera2D::get_camera_transform() { if (!get_tree()) return Matrix32(); Size2 screen_size = get_viewport_rect().size; screen_size=get_viewport_rect().size; Point2 new_camera_pos = get_global_transform().get_origin(); Point2 ret_camera_pos; if (!first) { if (anchor_mode==ANCHOR_MODE_DRAG_CENTER) { if (h_drag_enabled && !get_tree()->is_editor_hint()) { camera_pos.x = MIN( camera_pos.x, (new_camera_pos.x + screen_size.x * 0.5 * drag_margin[MARGIN_RIGHT])); camera_pos.x = MAX( camera_pos.x, (new_camera_pos.x - screen_size.x * 0.5 * drag_margin[MARGIN_LEFT])); } else { if (h_ofs<0) { camera_pos.x = new_camera_pos.x + screen_size.x * 0.5 * drag_margin[MARGIN_RIGHT] * h_ofs; } else { camera_pos.x = new_camera_pos.x + screen_size.x * 0.5 * drag_margin[MARGIN_LEFT] * h_ofs; } } if (v_drag_enabled && !get_tree()->is_editor_hint()) { camera_pos.y = MIN( camera_pos.y, (new_camera_pos.y + screen_size.y * 0.5 * drag_margin[MARGIN_BOTTOM])); camera_pos.y = MAX( camera_pos.y, (new_camera_pos.y - screen_size.y * 0.5 * drag_margin[MARGIN_TOP])); } else { if (v_ofs<0) { camera_pos.y = new_camera_pos.y + screen_size.y * 0.5 * drag_margin[MARGIN_TOP] * v_ofs; } else { camera_pos.y = new_camera_pos.y + screen_size.y * 0.5 * drag_margin[MARGIN_BOTTOM] * v_ofs; } } } else if (anchor_mode==ANCHOR_MODE_FIXED_TOP_LEFT){ camera_pos=new_camera_pos; } if (smoothing_enabled && !get_tree()->is_editor_hint()) { float c = smoothing*get_fixed_process_delta_time(); smoothed_camera_pos = ((camera_pos-smoothed_camera_pos)*c)+smoothed_camera_pos; ret_camera_pos=smoothed_camera_pos; // camera_pos=camera_pos*(1.0-smoothing)+new_camera_pos*smoothing; } else { ret_camera_pos=smoothed_camera_pos=camera_pos; } } else { ret_camera_pos=smoothed_camera_pos=camera_pos=new_camera_pos; first=false; } Point2 screen_offset = (anchor_mode==ANCHOR_MODE_DRAG_CENTER ? (screen_size * 0.5 * zoom) : Point2()); float angle = get_global_transform().get_rotation(); if(rotating){ screen_offset = screen_offset.rotated(angle); } Rect2 screen_rect(-screen_offset+ret_camera_pos,screen_size*zoom); if (screen_rect.pos.x + screen_rect.size.x > limit[MARGIN_RIGHT]) screen_rect.pos.x = limit[MARGIN_RIGHT] - screen_rect.size.x; if (screen_rect.pos.y + screen_rect.size.y > limit[MARGIN_BOTTOM]) screen_rect.pos.y = limit[MARGIN_BOTTOM] - screen_rect.size.y; if (screen_rect.pos.x < limit[MARGIN_LEFT]) screen_rect.pos.x=limit[MARGIN_LEFT]; if (screen_rect.pos.y < limit[MARGIN_TOP]) screen_rect.pos.y =limit[MARGIN_TOP]; if (offset!=Vector2()) { screen_rect.pos+=offset; if (screen_rect.pos.x + screen_rect.size.x > limit[MARGIN_RIGHT]) screen_rect.pos.x = limit[MARGIN_RIGHT] - screen_rect.size.x; if (screen_rect.pos.y + screen_rect.size.y > limit[MARGIN_BOTTOM]) screen_rect.pos.y = limit[MARGIN_BOTTOM] - screen_rect.size.y; if (screen_rect.pos.x < limit[MARGIN_LEFT]) screen_rect.pos.x=limit[MARGIN_LEFT]; if (screen_rect.pos.y < limit[MARGIN_TOP]) screen_rect.pos.y =limit[MARGIN_TOP]; } camera_screen_center=screen_rect.pos+screen_rect.size*0.5; Matrix32 xform; if(rotating){ xform.set_rotation(angle); } xform.scale_basis(zoom); xform.set_origin(screen_rect.pos/*.floor()*/); /* if (0) { xform = get_global_transform() * xform; } else { xform.elements[2]+=get_global_transform().get_origin(); } */ return (xform).affine_inverse(); }
S32 LLFontGL::render(const LLWString &wstr, const S32 begin_offset, const F32 x, const F32 y, const LLColor4 &color, const HAlign halign, const VAlign valign, U8 style, const S32 max_chars, S32 max_pixels, F32* right_x, BOOL use_embedded, BOOL use_ellipses) const { if(!sDisplayFont) //do not display texts { return wstr.length() ; } if (wstr.empty()) { return 0; } gGL.getTexUnit(0)->enable(LLTexUnit::TT_TEXTURE); S32 scaled_max_pixels = max_pixels == S32_MAX ? S32_MAX : llceil((F32)max_pixels * sScaleX); // Strip off any style bits that are already accounted for by the font. style = style & (~getFontDesc().getStyle()); F32 drop_shadow_strength = 0.f; if (style & (DROP_SHADOW | DROP_SHADOW_SOFT)) { F32 luminance; color.calcHSL(NULL, NULL, &luminance); drop_shadow_strength = clamp_rescale(luminance, 0.35f, 0.6f, 0.f, 1.f); if (luminance < 0.35f) { style = style & ~(DROP_SHADOW | DROP_SHADOW_SOFT); } } gGL.pushMatrix(); glLoadIdentity(); gGL.translatef(floorf(sCurOrigin.mX*sScaleX), floorf(sCurOrigin.mY*sScaleY), sCurOrigin.mZ); // this code snaps the text origin to a pixel grid to start with F32 pixel_offset_x = llround((F32)sCurOrigin.mX) - (sCurOrigin.mX); F32 pixel_offset_y = llround((F32)sCurOrigin.mY) - (sCurOrigin.mY); gGL.translatef(-pixel_offset_x, -pixel_offset_y, 0.f); LLFastTimer t(LLFastTimer::FTM_RENDER_FONTS); gGL.color4fv( color.mV ); S32 chars_drawn = 0; S32 i; S32 length; if (-1 == max_chars) { length = (S32)wstr.length() - begin_offset; } else { length = llmin((S32)wstr.length() - begin_offset, max_chars ); } F32 cur_x, cur_y, cur_render_x, cur_render_y; // Not guaranteed to be set correctly gGL.setSceneBlendType(LLRender::BT_ALPHA); cur_x = ((F32)x * sScaleX); cur_y = ((F32)y * sScaleY); // Offset y by vertical alignment. switch (valign) { case TOP: cur_y -= mAscender; break; case BOTTOM: cur_y += mDescender; break; case VCENTER: cur_y -= ((mAscender - mDescender)/2.f); break; case BASELINE: // Baseline, do nothing. break; default: break; } switch (halign) { case LEFT: break; case RIGHT: cur_x -= llmin(scaled_max_pixels, llround(getWidthF32(wstr.c_str(), 0, length) * sScaleX)); break; case HCENTER: cur_x -= llmin(scaled_max_pixels, llround(getWidthF32(wstr.c_str(), 0, length) * sScaleX)) / 2; break; default: break; } cur_render_y = cur_y; cur_render_x = cur_x; F32 start_x = cur_x; F32 inv_width = 1.f / mFontBitmapCachep->getBitmapWidth(); F32 inv_height = 1.f / mFontBitmapCachep->getBitmapHeight(); const S32 LAST_CHARACTER = LLFont::LAST_CHAR_FULL; BOOL draw_ellipses = FALSE; if (use_ellipses && halign == LEFT) { // check for too long of a string if (getWidthF32(wstr.c_str(), 0, max_chars) * sScaleX > scaled_max_pixels) { // use four dots for ellipsis width to generate padding const LLWString dots(utf8str_to_wstring(std::string("...."))); scaled_max_pixels = llmax(0, scaled_max_pixels - llround(getWidthF32(dots.c_str()))); draw_ellipses = TRUE; } } // Remember last-used texture to avoid unnecesssary bind calls. LLImageGL *last_bound_texture = NULL; for (i = begin_offset; i < begin_offset + length; i++) { llwchar wch = wstr[i]; // Handle embedded characters first, if they're enabled. // Embedded characters are a hack for notecards const embedded_data_t* ext_data = use_embedded ? getEmbeddedCharData(wch) : NULL; if (ext_data) { LLImageGL* ext_image = ext_data->mImage; const LLWString& label = ext_data->mLabel; F32 ext_height = (F32)ext_image->getHeight() * sScaleY; F32 ext_width = (F32)ext_image->getWidth() * sScaleX; F32 ext_advance = (EXT_X_BEARING * sScaleX) + ext_width; if (!label.empty()) { ext_advance += (EXT_X_BEARING + getFontExtChar()->getWidthF32( label.c_str() )) * sScaleX; } if (start_x + scaled_max_pixels < cur_x + ext_advance) { // Not enough room for this character. break; } if (last_bound_texture != ext_image) { gGL.getTexUnit(0)->bind(ext_image); last_bound_texture = ext_image; } // snap origin to whole screen pixel const F32 ext_x = (F32)llround(cur_render_x + (EXT_X_BEARING * sScaleX)); const F32 ext_y = (F32)llround(cur_render_y + (EXT_Y_BEARING * sScaleY + mAscender - mLineHeight)); LLRectf uv_rect(0.f, 1.f, 1.f, 0.f); LLRectf screen_rect(ext_x, ext_y + ext_height, ext_x + ext_width, ext_y); drawGlyph(screen_rect, uv_rect, LLColor4::white, style, drop_shadow_strength); if (!label.empty()) { gGL.pushMatrix(); //glLoadIdentity(); //gGL.translatef(sCurOrigin.mX, sCurOrigin.mY, 0.0f); //glScalef(sScaleX, sScaleY, 1.f); getFontExtChar()->render(label, 0, /*llfloor*/((ext_x + (F32)ext_image->getWidth() + EXT_X_BEARING) / sScaleX), /*llfloor*/(cur_y / sScaleY), color, halign, BASELINE, NORMAL, S32_MAX, S32_MAX, NULL, TRUE ); gGL.popMatrix(); } gGL.color4fv(color.mV); chars_drawn++; cur_x += ext_advance; if (((i + 1) < length) && wstr[i+1]) { cur_x += EXT_KERNING * sScaleX; } cur_render_x = cur_x; } else { if (!hasGlyph(wch)) { addChar(wch); } const LLFontGlyphInfo* fgi= getGlyphInfo(wch); if (!fgi) { llerrs << "Missing Glyph Info" << llendl; break; } // Per-glyph bitmap texture. LLImageGL *image_gl = mFontBitmapCachep->getImageGL(fgi->mBitmapNum); if (last_bound_texture != image_gl) { gGL.getTexUnit(0)->bind(image_gl); last_bound_texture = image_gl; } if ((start_x + scaled_max_pixels) < (cur_x + fgi->mXBearing + fgi->mWidth)) { // Not enough room for this character. break; } // Draw the text at the appropriate location //Specify vertices and texture coordinates LLRectf uv_rect((fgi->mXBitmapOffset) * inv_width, (fgi->mYBitmapOffset + fgi->mHeight + PAD_UVY) * inv_height, (fgi->mXBitmapOffset + fgi->mWidth) * inv_width, (fgi->mYBitmapOffset - PAD_UVY) * inv_height); // snap glyph origin to whole screen pixel LLRectf screen_rect(llround(cur_render_x + (F32)fgi->mXBearing), llround(cur_render_y + (F32)fgi->mYBearing), llround(cur_render_x + (F32)fgi->mXBearing) + (F32)fgi->mWidth, llround(cur_render_y + (F32)fgi->mYBearing) - (F32)fgi->mHeight); drawGlyph(screen_rect, uv_rect, color, style, drop_shadow_strength); chars_drawn++; cur_x += fgi->mXAdvance; cur_y += fgi->mYAdvance; llwchar next_char = wstr[i+1]; if (next_char && (next_char < LAST_CHARACTER)) { // Kern this puppy. if (!hasGlyph(next_char)) { addChar(next_char); } cur_x += getXKerning(wch, next_char); } // Round after kerning. // Must do this to cur_x, not just to cur_render_x, otherwise you // will squish sub-pixel kerned characters too close together. // For example, "CCCCC" looks bad. cur_x = (F32)llfloor(cur_x + 0.5f); //cur_y = (F32)llfloor(cur_y + 0.5f); cur_render_x = cur_x; cur_render_y = cur_y; } } if (right_x) { *right_x = cur_x / sScaleX; } if (style & UNDERLINE) { gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); gGL.begin(LLRender::LINES); gGL.vertex2f(start_x, cur_y - (mDescender)); gGL.vertex2f(cur_x, cur_y - (mDescender)); gGL.end(); } // *FIX: get this working in all alignment cases, etc. if (draw_ellipses) { // recursively render ellipses at end of string // we've already reserved enough room gGL.pushMatrix(); //glLoadIdentity(); //gGL.translatef(sCurOrigin.mX, sCurOrigin.mY, 0.0f); //glScalef(sScaleX, sScaleY, 1.f); renderUTF8(std::string("..."), 0, cur_x / sScaleX, (F32)y, color, LEFT, valign, style, S32_MAX, max_pixels, right_x, FALSE); gGL.popMatrix(); } gGL.popMatrix(); return chars_drawn; }
void Console::on_show() { const auto screen_r = screen_rect(); display.scroll_set_area(screen_r.top(), screen_r.bottom()); clear(); }
void RSSI::paint(Painter& painter) { const auto r = screen_rect(); constexpr int rssi_sample_range = 256; constexpr float rssi_voltage_min = 0.4; constexpr float rssi_voltage_max = 2.2; constexpr float adc_voltage_max = 3.3; constexpr int raw_min = rssi_sample_range * rssi_voltage_min / adc_voltage_max; constexpr int raw_max = rssi_sample_range * rssi_voltage_max / adc_voltage_max; constexpr int raw_delta = raw_max - raw_min; const range_t<int> x_avg_range { 0, r.width() - 1 }; const auto x_avg = x_avg_range.clip((avg_ - raw_min) * r.width() / raw_delta); const range_t<int> x_min_range { 0, x_avg }; const auto x_min = x_min_range.clip((min_ - raw_min) * r.width() / raw_delta); const range_t<int> x_max_range { x_avg + 1, r.width() }; const auto x_max = x_max_range.clip((max_ - raw_min) * r.width() / raw_delta); const Rect r0 { static_cast<ui::Coord>(r.left()), r.top(), static_cast<ui::Dim>(x_min), r.height() }; painter.fill_rectangle( r0, Color::blue() ); const Rect r1 { static_cast<ui::Coord>(r.left() + x_min), r.top(), static_cast<ui::Dim>(x_avg - x_min), r.height() }; painter.fill_rectangle( r1, Color::red() ); const Rect r2 { static_cast<ui::Coord>(r.left() + x_avg), r.top(), 1, r.height() }; painter.fill_rectangle( r2, Color::white() ); const Rect r3 { static_cast<ui::Coord>(r.left() + x_avg + 1), r.top(), static_cast<ui::Dim>(x_max - (x_avg + 1)), r.height() }; painter.fill_rectangle( r3, Color::red() ); const Rect r4 { static_cast<ui::Coord>(r.left() + x_max), r.top(), static_cast<ui::Dim>(r.width() - x_max), r.height() }; painter.fill_rectangle( r4, Color::black() ); if (pwmrssi_enabled) { const range_t<int> pwmrssi_avg_range { 0, 96 }; const auto pwmrssi_avg = pwmrssi_avg_range.clip((avg_ - raw_min) * 96 / raw_delta); baseband::set_pwmrssi(pwmrssi_avg, true); } }