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