tomwalters@411: // Copyright 2007-2010, Thomas Walters, Willem van Engen tomwalters@397: // tomwalters@397: // AIM-C: A C++ implementation of the Auditory Image Model tomwalters@397: // http://www.acousticscale.org/AIMC tomwalters@397: // tomwalters@397: // Licensed under the Apache License, Version 2.0 (the "License"); tomwalters@397: // you may not use this file except in compliance with the License. tomwalters@397: // You may obtain a copy of the License at tomwalters@397: // tomwalters@397: // http://www.apache.org/licenses/LICENSE-2.0 tomwalters@397: // tomwalters@397: // Unless required by applicable law or agreed to in writing, software tomwalters@397: // distributed under the License is distributed on an "AS IS" BASIS, tomwalters@397: // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. tomwalters@397: // See the License for the specific language governing permissions and tomwalters@397: // limitations under the License. tomwalters@397: tomwalters@397: /*! tomwalters@397: * \file tomwalters@397: * \brief Output device for output to a graphics file using cairo (LGPL) tomwalters@397: * tomwalters@397: * \author Tom Walters and Willem van Engen tomwalters@397: * \date created 2007/09/17 tomwalters@397: * \version \$Header: $ tomwalters@397: */ tomwalters@397: tomwalters@397: #include "Support/Common.h" tomwalters@397: tomwalters@397: #include tomwalters@397: #include tomwalters@397: #include tomwalters@397: #include tomwalters@397: #include tomwalters@412: #include tomwalters@397: tomwalters@444: #ifdef _WINDOWS tomwalters@444: # include // for _mkdir&_rmdir tomwalters@445: #endif tomwalters@444: tomwalters@412: //#include "cairo-quartz.h" tomwalters@411: tom@399: #include "Modules/Output/Graphics/Devices/GraphicsOutputDeviceCairo.h" tom@399: tom@399: namespace aimc { tomwalters@397: tomwalters@397: GraphicsOutputDeviceCairo::GraphicsOutputDeviceCairo(Parameters *pParam) tom@399: : GraphicsOutputDevice(pParam) { tomwalters@398: m_bOutputFile = false; tomwalters@398: m_iFileNumber = 0; tomwalters@398: m_iVertexType = VertexTypeNone; tomwalters@398: m_bUseMemoryBuffer=false; tomwalters@443: parameters_->DefaultString("output.img.format", "png"); tomwalters@443: } tomwalters@443: tomwalters@443: void GraphicsOutputDeviceCairo::Reset(Parameters* global_parameters) { tomwalters@443: Initialize(global_parameters); tomwalters@443: } tomwalters@443: tomwalters@443: bool GraphicsOutputDeviceCairo::Initialize(Parameters *global_parameters) { tomwalters@443: global_parameters_ = global_parameters; tomwalters@443: #ifdef _WINDOWS tomwalters@443: string pathsep("\\"); tomwalters@443: #else tomwalters@443: string pathsep("/"); tomwalters@443: #endif tomwalters@443: directory_ = global_parameters->GetString("output_filename_base") + pathsep; tomwalters@443: //! \todo Make build system check for mkdtemp() to use it when available. See TODO.txt. tomwalters@443: #ifdef _WINDOWS tomwalters@446: _mkdir(directory_.c_str()); tomwalters@443: #else tomwalters@443: mkdir(directory_.c_str(), S_IRWXU); tomwalters@443: #endif tomwalters@443: InitialzeInternal(); tomwalters@443: return true; tomwalters@397: } tomwalters@397: tomwalters@411: bool GraphicsOutputDeviceCairo::Initialize(string directory) { tomwalters@411: directory_ = directory; tomwalters@443: InitialzeInternal(); tomwalters@397: tomwalters@398: /* Try to open an image to see if everything is allright. We want to avoid tomwalters@398: * errors in the main Process()ing loop. */ tomwalters@411: /*if (!OpenFile(0)) { tomwalters@398: //! \todo Better error message that is more specific about the cause. tom@400: LOG_ERROR(_T("Could not open output directory '%s' using graphics format '%s'."), tomwalters@411: directory_.c_str(), parameters_->DefaultString("output.img.format", "png")); tomwalters@398: return false; tomwalters@398: } tomwalters@411: CloseFile();*/ tomwalters@397: tomwalters@398: return true; tomwalters@397: } tomwalters@397: tomwalters@411: /*bool GraphicsOutputDeviceCairo::Initialize() { tomwalters@397: Init(); tomwalters@397: m_bUseMemoryBuffer = true; tomwalters@397: return(true); tomwalters@411: }*/ tomwalters@397: tomwalters@443: void GraphicsOutputDeviceCairo::InitialzeInternal() { tomwalters@411: AIM_ASSERT(parameters_); tomwalters@397: tomwalters@411: parameters_->DefaultString("output.img.color.background", "black"); tomwalters@397: tomwalters@411: m_bInvertColors = parameters_->DefaultBool("output.img.color.invert", "false"); tomwalters@411: tomwalters@411: // Output size. tomwalters@411: m_iWidth = parameters_->DefaultInt("output.img.width", 800); tomwalters@411: m_iHeight = parameters_->DefaultInt("output.img.height", 600); tomwalters@411: tomwalters@411: // Cairo's RGB24 format has 32-bit pixels with the upper 8 bits unused. tomwalters@411: // This is not the same as the plotutils PNG format. This information is transferred by the tomwalters@411: // function GetPixelFormat. The pixel format is dealt with by the reciever. tomwalters@412: m_cSurface = cairo_image_surface_create(CAIRO_FORMAT_RGB24, tomwalters@411: m_iWidth, tomwalters@411: m_iHeight); tomwalters@411: m_cCr = cairo_create(m_cSurface); tomwalters@411: cairo_scale(m_cCr, (float)m_iWidth, (float)m_iHeight); tomwalters@411: // Now setup things for this plotter. tomwalters@411: cairo_select_font_face(m_cCr, tomwalters@411: parameters_->DefaultString("output.img.fontname", tomwalters@411: "HersheySans"), tomwalters@411: CAIRO_FONT_SLANT_NORMAL, tomwalters@411: CAIRO_FONT_WEIGHT_BOLD); tomwalters@411: cairo_set_font_size (m_cCr, 0.02); tomwalters@397: } tomwalters@397: tomwalters@397: unsigned char* GraphicsOutputDeviceCairo::GetBuffer() { tomwalters@397: if(m_bUseMemoryBuffer) tomwalters@397: return (cairo_image_surface_get_data (m_cSurface)); tomwalters@397: else tomwalters@397: return NULL; tomwalters@397: } tomwalters@397: tomwalters@397: bool GraphicsOutputDeviceCairo::OpenFile(unsigned int index) { tomwalters@411: const char *strPlottype = parameters_->GetString("output.img.format"); tomwalters@397: if (!m_bUseMemoryBuffer) { tomwalters@397: struct stat fileinfo; tomwalters@397: // Get filename without trailing slash tomwalters@411: char filename[PATH_MAX]; tomwalters@411: strncpy(filename, directory_.c_str(), sizeof(filename)/sizeof(filename[0])); tomwalters@397: #ifdef _WINDOWS tomwalters@411: if (filename[strlen(filename)-1]=='\\') { tomwalters@411: filename[strlen(filename)-1]='\0'; tomwalters@397: } tomwalters@397: #else tomwalters@411: if (filename[strlen(filename)-1]=='/') { tomwalters@411: filename[strlen(filename)-1]='\0'; tomwalters@397: } tomwalters@397: #endif tomwalters@397: // Enumerate files it m_sDir is a directory. tomwalters@411: if (stat(filename, &fileinfo) == 0 && (fileinfo.st_mode & S_IFDIR)) { tomwalters@397: // We have a directory: enumerate with index tomwalters@411: snprintf(filename, sizeof(filename) / sizeof(filename[0]), tomwalters@411: "%s%06d.%s", tomwalters@411: directory_.c_str(), tomwalters@397: index, tomwalters@397: strPlottype); tomwalters@397: // If type is 'auto', fallback to 'png' tomwalters@397: if (strcmp(strPlottype, "auto")==0) tomwalters@397: strPlottype = "png"; tomwalters@397: } else { tomwalters@397: // We have a (probably non-existant) file. Auto-detect type by extension if requested tomwalters@411: strncpy(filename, tomwalters@411: directory_.c_str(), tomwalters@411: sizeof(filename)/sizeof(filename[0])); tomwalters@411: char *pDot = strrchr(filename, '.'); tomwalters@397: if (!pDot) { tom@400: LOG_ERROR(_T("Please supply extension on filename when using 'auto' format: '%s'"), tomwalters@411: filename); tomwalters@397: return false; tomwalters@397: } tomwalters@397: strPlottype = &pDot[1]; tomwalters@397: } tomwalters@411: image_filename_ = filename; tomwalters@397: m_bOutputFile= true; //! \todo Should check that it's possible to write to the file tomwalters@397: } tomwalters@411: tomwalters@398: return true; tomwalters@397: } tomwalters@397: tomwalters@397: void GraphicsOutputDeviceCairo::CloseFile() { tomwalters@398: // And the output file tomwalters@398: if (m_bOutputFile) { tomwalters@411: cairo_surface_write_to_png(m_cSurface, image_filename_.c_str()); tomwalters@398: m_bOutputFile = false; tomwalters@398: } tomwalters@411: cairo_set_source_rgb (m_cCr, 0.0, 0.0, 0.0); tomwalters@411: cairo_paint (m_cCr); tomwalters@411: //cairo_destroy(m_cCr); tomwalters@411: //cairo_surface_destroy(m_cSurface); tomwalters@397: } tomwalters@397: tomwalters@397: GraphicsOutputDeviceCairo::~GraphicsOutputDeviceCairo() { tomwalters@398: AIM_ASSERT(!m_iPlotHandle); tomwalters@398: CloseFile(); tomwalters@397: } tomwalters@397: tomwalters@397: void GraphicsOutputDeviceCairo::gGrab() { tomwalters@397: // Open file. tomwalters@398: if (!OpenFile(m_iFileNumber)) { tomwalters@398: return; tomwalters@397: } tomwalters@398: // Setup plotting area. tomwalters@398: cairo_set_line_width (m_cCr, 0.001f); tomwalters@398: gColor3f (0.0f, 0.0f, 0.0f); tomwalters@397: cairo_paint (m_cCr); tomwalters@397: gColor3f(1.0f, 1.0f, 0.0f); tomwalters@397: } tomwalters@397: tomwalters@397: int GraphicsOutputDeviceCairo::GetPixelFormat() { tomwalters@397: return AIM_PIX_FMT_RGB24_32; tomwalters@397: } tomwalters@397: tomwalters@397: void GraphicsOutputDeviceCairo::gBeginLineStrip() { tomwalters@398: m_bIsFirstVertex = true; tomwalters@398: m_iVertexType = VertexTypeLine; tomwalters@398: //! \todo Make line width user-settable tomwalters@398: cairo_set_line_width (m_cCr, 0.001f); tomwalters@397: } tomwalters@397: tomwalters@397: void GraphicsOutputDeviceCairo::gBeginQuadStrip() { tomwalters@398: m_bIsFirstVertex = true; tomwalters@398: m_iVertexType = VertexTypeQuad; tomwalters@398: m_iPrevVertexCount = 0; tomwalters@397: cairo_set_line_width (m_cCr, 0.001f); tomwalters@397: } tomwalters@397: tomwalters@397: void GraphicsOutputDeviceCairo::gColor3f(float r, float g, float b) { tomwalters@397: if (m_bInvertColors) { tomwalters@411: r = 1.0 - r; tomwalters@411: g = 1.0 - g; tomwalters@411: b = 1.0 - b; tomwalters@398: } tomwalters@397: cairo_set_source_rgb (m_cCr, r, g, b); tomwalters@397: } tomwalters@397: tomwalters@397: void GraphicsOutputDeviceCairo::gVertex3f(float x, float y, float z) { tomwalters@398: switch(m_iVertexType) { tomwalters@398: case VertexTypeLine: tomwalters@398: if (m_bIsFirstVertex) { tomwalters@398: m_bIsFirstVertex = false; tomwalters@398: //pl_fmove(x, y); tomwalters@411: cairo_move_to(m_cCr, x, 1.0 - y); tomwalters@398: } else { tomwalters@398: //pl_fcont(x, y); tomwalters@411: cairo_line_to(m_cCr, x, 1.0 - y); tomwalters@398: } tomwalters@398: break; tomwalters@398: case VertexTypeQuad: tomwalters@411: /* Store vertices until we have four in a row. tomwalters@398: * The order of vertices when processing quads is: tomwalters@398: * 1-----3-----5 tomwalters@398: * | | | tomwalters@398: * 0-----2-----4 tomwalters@398: */ tomwalters@398: if (m_iPrevVertexCount >= 3) { tomwalters@398: // Plot this quad tomwalters@411: //cairo_set_source_rgb(m_cCr, 0.2, 1 - m_aPrevY[0], m_aPrevX[0]); tomwalters@411: cairo_rectangle (m_cCr, m_aPrevX[2], tomwalters@411: 1 - m_aPrevY[2], m_aPrevX[2] - m_aPrevX[0], tomwalters@411: y - m_aPrevY[2]); tomwalters@411: cairo_fill (m_cCr); tomwalters@443: tomwalters@411: /*cairo_move_to(m_cCr, , ); tomwalters@411: cairo_line_to(m_cCr, , 1 - m_aPrevY[1]); tomwalters@398: cairo_line_to(m_cCr, x, y); tomwalters@411: cairo_line_to(m_cCr, m_aPrevX[2], 1 - m_aPrevY[2]);*/ tomwalters@397: tomwalters@398: // Last vertices of this quad are the first of the next tomwalters@398: m_aPrevX[0] = m_aPrevX[2]; tomwalters@398: m_aPrevY[0] = m_aPrevY[2]; tomwalters@398: m_aPrevX[1] = x; tomwalters@398: m_aPrevY[1] = y; tomwalters@398: m_iPrevVertexCount = 2; tomwalters@398: } else { tomwalters@398: // Not at the fourth, keep storing tomwalters@398: m_aPrevX[m_iPrevVertexCount] = x; tomwalters@398: m_aPrevY[m_iPrevVertexCount] = y; tomwalters@398: m_iPrevVertexCount++; tomwalters@398: } tomwalters@398: break; tomwalters@398: default: tomwalters@398: // Should not happen tomwalters@398: AIM_ASSERT(0); tomwalters@398: } tomwalters@397: } tomwalters@397: tomwalters@397: void GraphicsOutputDeviceCairo::gEnd() { tomwalters@398: if(m_iVertexType==VertexTypeLine) tomwalters@397: cairo_stroke (m_cCr); tomwalters@397: else tomwalters@397: cairo_fill (m_cCr); tomwalters@398: m_iVertexType = VertexTypeNone; tomwalters@397: } tomwalters@397: tomwalters@397: void GraphicsOutputDeviceCairo::gText3f(float x, tomwalters@397: float y, tomwalters@397: float z, tomwalters@397: const char *sStr, tomwalters@397: bool bRotated) { tomwalters@402: //cairo_text_extents_t te; tomwalters@398: if (bRotated) { tomwalters@398: cairo_rotate(m_cCr, M_PI/2); tomwalters@411: //cairo_move_to(m_cCr, x ,1-y); tomwalters@398: cairo_show_text(m_cCr, sStr); tomwalters@398: //cairo_identity_matrix(m_cCr); tomwalters@398: cairo_rotate(m_cCr, -M_PI/2); tomwalters@398: } else { tomwalters@398: cairo_move_to(m_cCr, x ,1-y); tomwalters@398: cairo_show_text(m_cCr, sStr); tomwalters@398: } tomwalters@397: } tomwalters@397: tomwalters@397: void GraphicsOutputDeviceCairo::gRelease() { tomwalters@398: AIM_ASSERT(m_iPlotHandle>0); tomwalters@398: CloseFile(); tomwalters@398: // Finished this one, up to the next! tomwalters@398: m_iFileNumber++; tomwalters@397: } tom@399: } // namespace aimc