/*========================================================================= Program: Visualization Toolkit Module: $RCSfile: vtkTextActor3D.cxx,v $ Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen All rights reserved. See Copyright.txt or http://www.kitware.com/Copyright.htm for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notice for more information. =========================================================================*/ #include "vtkTextActor3D.h" #include "vtkObjectFactory.h" #include "vtkCamera.h" #include "vtkImageActor.h" #include "vtkImageData.h" #include "vtkTransform.h" #include "vtkTextProperty.h" #include "vtkRenderer.h" #include "vtkWindow.h" #include "vtkFreeTypeUtilities.h" #include "vtkMatrix4x4.h" #include "vtkMath.h" vtkCxxRevisionMacro(vtkTextActor3D, "$Revision: 1.5 $"); vtkStandardNewMacro(vtkTextActor3D); vtkCxxSetObjectMacro(vtkTextActor3D, TextProperty, vtkTextProperty); // ---------------------------------------------------------------------------- vtkTextActor3D::vtkTextActor3D() { this->Input = NULL; this->ImageActor = vtkImageActor::New(); this->ImageData = NULL; this->TextProperty = NULL; this->BuildTime.Modified(); this->SetTextProperty(vtkTextProperty::New()); this->TextProperty->Delete(); this->ImageActor->InterpolateOn(); } // -------------------------------------------------------------------------- vtkTextActor3D::~vtkTextActor3D() { this->SetTextProperty(NULL); this->SetInput(NULL); if (this->ImageActor) { this->ImageActor->Delete(); this->ImageActor = NULL; } if (this->ImageData) { this->ImageData->Delete(); this->ImageData = NULL; } } // -------------------------------------------------------------------------- void vtkTextActor3D::ShallowCopy(vtkProp *prop) { vtkTextActor3D *a = vtkTextActor3D::SafeDownCast(prop); if (a != NULL) { this->SetInput(a->GetInput()); this->SetTextProperty(a->GetTextProperty()); } this->Superclass::ShallowCopy(prop); } // -------------------------------------------------------------------------- double* vtkTextActor3D::GetBounds() { if (this->ImageActor) { return this->ImageActor->GetBounds(); } return NULL; } // -------------------------------------------------------------------------- void vtkTextActor3D::ReleaseGraphicsResources(vtkWindow *win) { if (this->ImageActor) { this->ImageActor->ReleaseGraphicsResources(win); } this->Superclass::ReleaseGraphicsResources(win); } // -------------------------------------------------------------------------- int vtkTextActor3D::RenderOverlay(vtkViewport *viewport) { int rendered_something = 0; if (this->UpdateImageActor() && this->ImageActor) { rendered_something += this->ImageActor->RenderOverlay(viewport); } return rendered_something; } // -------------------------------------------------------------------------- int vtkTextActor3D::RenderTranslucentGeometry(vtkViewport *viewport) { int rendered_something = 0; if (this->UpdateImageActor() && this->ImageActor) { rendered_something += this->ImageActor->RenderTranslucentGeometry(viewport); } return rendered_something; } // -------------------------------------------------------------------------- int vtkTextActor3D::RenderOpaqueGeometry(vtkViewport *viewport) { int rendered_something = 0; if (this->UpdateImageActor() && this->ImageActor) { rendered_something += this->ImageActor->RenderOpaqueGeometry(viewport); } return rendered_something; } // -------------------------------------------------------------------------- int vtkTextActor3D::UpdateImageActor() { // Need text prop if (!this->TextProperty) { vtkErrorMacro(<<"Need a text property to render text actor"); return 0; } // No input, the assign the image actor a zilch input if (!this->Input || !*this->Input) { if (this->ImageActor) { this->ImageActor->SetInput(NULL); } return 1; } // Do we need to (re-)render the text ? // Yes if: // - instance has been modified since last build // - text prop has been modified since last build // - ImageData ivar has not been allocated yet if (this->GetMTime() > this->BuildTime || this->TextProperty->GetMTime() > this->BuildTime || !this->ImageData) { //cout << "Need to re-render " << this->GetMTime() << ", " << this->TextProperty->GetMTime() << ", " << this->BuildTime << endl; this->BuildTime.Modified(); // Get the bounding box of the text to render // If the bounding box is invalid, then there is nothing to render // assign the image actor a nada input vtkFreeTypeUtilities *fu = vtkFreeTypeUtilities::GetInstance(); if (!fu) { vtkErrorMacro(<<"Failed getting the FreeType utilities instance"); return 0; } int text_bbox[4]; fu->GetBoundingBox(this->TextProperty, this->Input, text_bbox); if (!fu->IsBoundingBoxValid(text_bbox)) { if (this->ImageActor) { this->ImageActor->SetInput(NULL); } return 1; } // The bounding box was the area that is going to be filled with pixels // given a text origin of (0, 0). Now get the real size we need, i.e. // the full extent from the origin to the bounding box. int text_size[2]; text_size[0] = (text_bbox[1] - text_bbox[0] + 1) + abs(text_bbox[0]); text_size[1] = (text_bbox[3] - text_bbox[2] + 1) + abs(text_bbox[2]); // If the RGBA image data is too small, resize it to the next power of 2 // WARNING: at this point, since this image is going to be a texture // we should limit its size or query the hardware if (!this->ImageData) { this->ImageData = vtkImageData::New(); this->ImageData->SetScalarTypeToUnsignedChar(); this->ImageData->SetNumberOfScalarComponents(4); this->ImageData->SetSpacing(1.0, 1.0, 1.0); } // If the current image data is too small to render the text, // or more than twice as big (too hungry), then resize int img_dims[3], new_img_dims[3]; this->ImageData->GetDimensions(img_dims); if (img_dims[0] < text_size[0] || img_dims[1] < text_size[1] || text_size[0] * 2 < img_dims[0] || text_size[1] * 2 < img_dims[0]) { new_img_dims[0] = 1 << (int)ceil(log((double)text_size[0]) / log(2.0)); new_img_dims[1] = 1 << (int)ceil(log((double)text_size[1]) / log(2.0)); new_img_dims[2] = 1; if (new_img_dims[0] != img_dims[0] || new_img_dims[1] != img_dims[1] || new_img_dims[2] != img_dims[2]) { //cout << "I need: " << text_size[0] << "x" << text_size[1] << ", I resize to: " << new_img_dims[0] << "x" << new_img_dims[1] << endl; this->ImageData->SetDimensions(new_img_dims); this->ImageData->AllocateScalars(); this->ImageData->UpdateInformation(); this->ImageData->SetUpdateExtent(this->ImageData->GetWholeExtent()); this->ImageData->PropagateUpdateExtent(); if (this->ImageActor) { this->ImageActor->SetDisplayExtent( this->ImageData->GetWholeExtent()); } } } // Render inside the image data int x = (text_bbox[0] < 0 ? -text_bbox[0] : 0); int y = (text_bbox[2] < 0 ? -text_bbox[2] : 0); #if 0 char *ptr = (char*)this->ImageData->GetScalarPointer(); int nb_comp = this->ImageData->GetNumberOfScalarComponents(); char *ptr_end = ptr + this->ImageData->GetNumberOfPoints() * nb_comp; memset(ptr, 0, ptr_end - ptr); ptr += nb_comp - 1; while (ptr < ptr_end) { *ptr = 255.0; ptr += nb_comp; } #else memset(this->ImageData->GetScalarPointer(), 0, (this->ImageData->GetNumberOfPoints() * this->ImageData->GetNumberOfScalarComponents())); #endif if (!fu->RenderString( this->TextProperty, this->Input, x, y, this->ImageData)) { vtkErrorMacro(<<"Failed rendering text to buffer"); return 0; } // Set the image data origin so that oriented text is placed properly // Also take shadow into account, we want the text to be on the baseline if (this->TextProperty->GetShadow()) { // this is wrong //x += this->TextProperty->GetShadowOffset()[0]; //y += this->TextProperty->GetShadowOffset()[1]; } this->ImageData->SetOrigin(-x, -y, 0); // Associate the image data (should be up to date now) to the image actor if (this->ImageActor) { this->ImageActor->SetInput(this->ImageData); } } // Position the actor if (this->ImageActor) { // Which one is faster ? #if 1 vtkMatrix4x4 *matrix = vtkMatrix4x4::New(); this->GetMatrix(matrix); this->ImageActor->SetUserMatrix(matrix); matrix->Delete(); #else this->ImageActor->SetPosition(this->GetPosition()); this->ImageActor->SetScale(this->GetScale()); this->ImageActor->SetOrientation(this->GetOrientation()); #endif } return 1; } // -------------------------------------------------------------------------- void vtkTextActor3D::PrintSelf(ostream& os, vtkIndent indent) { this->Superclass::PrintSelf(os,indent); os << indent << "Input: " << (this->Input ? this->Input : "(none)") << "\n"; if (this->TextProperty) { os << indent << "Text Property:\n"; this->TextProperty->PrintSelf(os, indent.GetNextIndent()); } else { os << indent << "Text Property: (none)\n"; } }