<commit_before>//==========================================================================
// Name:            fdmdv2_plot_waterfall.cpp
// Purpose:         Implements a waterfall plot derivative of fdmdv2_plot.
// Created:         June 22, 2012
// Authors:         David Rowe, David Witten
//
// License:
//
//  This program is free software; you can redistribute it and/or modify
//  it under the terms of the GNU Lesser General Public License version 2.1,
//  as published by the Free Software Foundation.  This program is
//  distributed in the hope that it will be useful, but WITHOUT ANY
//  WARRANTY; without even the implied warranty of MERCHANTABILITY or
//  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
//  License for more details.
//
//  You should have received a copy of the GNU Lesser General Public License
//  along with this program; if not, see <http://www.gnu.org/licenses/>.
//
//==========================================================================
#include <string.h>
#include "wx/wx.h"
#include "fdmdv2_main.h"

extern float g_avmag[];                 // av mag spec passed in to draw()
void fdmdv2_clickTune(float frequency); // callback to pass new click freq

BEGIN_EVENT_TABLE(PlotWaterfall, PlotPanel)
    EVT_PAINT           (PlotWaterfall::OnPaint)
    EVT_MOTION          (PlotWaterfall::OnMouseMove)
    EVT_LEFT_DOWN       (PlotWaterfall::OnMouseDown)
    EVT_LEFT_UP         (PlotWaterfall::OnMouseUp)
    EVT_MOUSEWHEEL      (PlotWaterfall::OnMouseWheelMoved)
    EVT_SIZE            (PlotWaterfall::OnSize)
    EVT_SHOW            (PlotWaterfall::OnShow)
END_EVENT_TABLE()

//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-=
// Class WaterfallPlot
//
// @class   WaterfallPlot
// @author  David Witten
// @date    $(Date)
// @file    $(CurrentFileName).$(CurrentFileExt)
// @brief
//
//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-=
PlotWaterfall::PlotWaterfall(wxFrame* parent): PlotPanel(parent)
{

    for(int i = 0; i < 255; i++)
    {
        m_heatmap_lut[i] = heatmap((float)i, 0.0, 255.0);
    }
    m_greyscale     = 0;
    m_Bufsz         = GetMaxClientSize();
    m_newdata       = false;
    m_firstPass     = true;
    m_line_color    = 0;
    SetLabelSize(10.0);

    m_pBmp = NULL;
}

// When the window size gets set we can work outthe size of the window
// we plot in and allocate a bit map of the correct size

void PlotWaterfall::OnSize(wxSizeEvent& event) {
    // resize bit map

    delete m_pBmp;

    m_rCtrl  = GetClientRect();

    // m_rGrid is coords of inner window we actually plot to.  We deflate it a bit
    // to leave room for axis labels.

    m_rGrid  = m_rCtrl;
    m_rGrid = m_rGrid.Deflate(PLOT_BORDER + (XLEFT_OFFSET/2), (PLOT_BORDER + (YBOTTOM_OFFSET/2)));

    // we want a bit map the size of m_rGrid

    m_pBmp = new wxBitmap(m_rGrid.GetWidth(), m_rGrid.GetHeight(), 24);
}

//----------------------------------------------------------------
// paintEvent()
//
// @class $(Name)
// @author $(User)
// @date $(Date)
// @file $(CurrentFileName).$(CurrentFileExt)
// @brief
//
// Called by the system of by wxWidgets when the panel needs
// to be redrawn. You can also trigger this call by calling
// Refresh()/Update().
//----------------------------------------------------------------
void PlotWaterfall::OnPaint(wxPaintEvent & evt)
{
    wxAutoBufferedPaintDC dc(this);
    draw(dc);
}

//----------------------------------------------------------------
// OnShow()
//----------------------------------------------------------------
void PlotWaterfall::OnShow(wxShowEvent& event)
{
}

//----------------------------------------------------------------
// ~PlotWaterfall()
//----------------------------------------------------------------
PlotWaterfall::~PlotWaterfall()
{
}

//----------------------------------------------------------------
// heatmap()
// map val to a rgb colour
// from http://eddiema.ca/2011/01/21/c-sharp-heatmaps/
//----------------------------------------------------------------
unsigned PlotWaterfall::heatmap(float val, float min, float max)
{
    unsigned r = 0;
    unsigned g = 0;
    unsigned b = 0;

    val = (val - min) / (max - min);
    if(val <= 0.2)
    {
        b = (unsigned)((val / 0.2) * 255);
    }
    else if(val >  0.2 &&  val <= 0.7)
    {
        b = (unsigned)((1.0 - ((val - 0.2) / 0.5)) * 255);
    }
    if(val >= 0.2 &&  val <= 0.6)
    {
        g = (unsigned)(((val - 0.2) / 0.4) * 255);
    }
    else if(val >  0.6 &&  val <= 0.9)
    {
        g = (unsigned)((1.0 - ((val - 0.6) / 0.3)) * 255);
    }
    if(val >= 0.5)
    {
        r = (unsigned)(((val - 0.5) / 0.5) * 255);
    }
    //printf("%f %x %x %x\n", val, r, g, b);
    return  (b << 16) + (g << 8) + r;
}

//----------------------------------------------------------------
// draw()
//----------------------------------------------------------------
void PlotWaterfall::draw(wxAutoBufferedPaintDC& dc)
{
    m_rCtrl  = GetClientRect();

    // m_rGrid is coords of inner window we actually plot to.  We deflate it a bit
    // to leave room for axis labels.

    m_rGrid = m_rCtrl;
    m_rGrid = m_rGrid.Deflate(PLOT_BORDER + (XLEFT_OFFSET/2), (PLOT_BORDER + (YBOTTOM_OFFSET/2)));

    if (m_pBmp == NULL) {
	// we want a bit map the size of m_rGrid

	m_pBmp = new wxBitmap(m_rGrid.GetWidth(), m_rGrid.GetHeight(), 24);
    }

    dc.Clear();

    if(m_newdata)
    {
        m_newdata = false;
        plotPixelData();
	dc.DrawBitmap(*m_pBmp, PLOT_BORDER + XLEFT_OFFSET, PLOT_BORDER);
    }
    else {

	// no data to plot so just erase to black.  Blue looks nicer
	// but is same colour as low amplitude signal

	// Bug on Linux: When Stop is pressed this code doesn't erase
	// the lower 25% of the Waterfall Window

	m_rPlot = wxRect(PLOT_BORDER + XLEFT_OFFSET, PLOT_BORDER, m_rGrid.GetWidth(), m_rGrid.GetHeight());
	wxBrush ltGraphBkgBrush = wxBrush(BLACK_COLOR);
	dc.SetBrush(ltGraphBkgBrush);
	dc.SetPen(wxPen(BLACK_COLOR, 0));
	dc.DrawRectangle(m_rPlot);
    }

    drawGraticule(dc);

}

//-------------------------------------------------------------------------
// drawGraticule()
//-------------------------------------------------------------------------
void PlotWaterfall::drawGraticule(wxAutoBufferedPaintDC& dc)
{
    int      x, y, text_w, text_h;
    char     buf[15];
    wxString s;
    float    f, time, freq_hz_to_px, time_s_to_py;

    wxBrush ltGraphBkgBrush;
    ltGraphBkgBrush.SetStyle(wxBRUSHSTYLE_TRANSPARENT);
    ltGraphBkgBrush.SetColour(*wxBLACK);
    dc.SetBrush(ltGraphBkgBrush);
    dc.SetPen(wxPen(BLACK_COLOR, 1));

    freq_hz_to_px = (float)m_rGrid.GetWidth()/(MAX_F_HZ-MIN_F_HZ);
    time_s_to_py = (float)m_rGrid.GetHeight()/WATERFALL_SECS_Y;

    // upper LH coords of plot area are (PLOT_BORDER + XLEFT_OFFSET, PLOT_BORDER)
    // lower RH coords of plot area are (PLOT_BORDER + XLEFT_OFFSET + m_rGrid.GetWidth(),
    //                                   PLOT_BORDER + m_rGrid.GetHeight())

    // Vertical gridlines

    dc.SetPen(m_penShortDash);
    for(f=STEP_F_HZ; f<MAX_F_HZ; f+=STEP_F_HZ) {
	x = f*freq_hz_to_px;
	x += PLOT_BORDER + XLEFT_OFFSET;
        dc.DrawLine(x, m_rGrid.GetHeight() + PLOT_BORDER, x, PLOT_BORDER);
        sprintf(buf, "%4.0fHz", f);
	GetTextExtent(buf, &text_w, &text_h);
        dc.DrawText(buf, x - text_w/2, m_rGrid.GetHeight() + PLOT_BORDER + YBOTTOM_TEXT_OFFSET);
    }

    // Horizontal gridlines

    dc.SetPen(m_penDotDash);
    for(time=0; time<=WATERFALL_SECS_Y; time+=WATERFALL_SECS_STEP) {
	y = m_rGrid.GetHeight() - time*time_s_to_py;
	y += PLOT_BORDER;
	dc.DrawLine(PLOT_BORDER + XLEFT_OFFSET, y,
		    (m_rGrid.GetWidth() + PLOT_BORDER + XLEFT_OFFSET), y);
        sprintf(buf, "%3.0fs", time);
	GetTextExtent(buf, &text_w, &text_h);
        dc.DrawText(buf, PLOT_BORDER + XLEFT_OFFSET - text_w - XLEFT_TEXT_OFFSET, y-text_h/2);
   }

}

//-------------------------------------------------------------------------
// plotPixelData()
//-------------------------------------------------------------------------
void PlotWaterfall::plotPixelData()
{
    float       spec_index_per_px;
    float       intensity_per_dB;
    float       px_per_sec;
    int         index;
    int         dy;
    int         dy_blocks;
    int         b;
    int         px;
    int         py;
    int         intensity;

    /*
      Design Notes:

      The height in pixels represents WATERFALL_SECS_Y of data.  Every DT
      seconds we get a vector of FDMDV_NSPEC spectrum samples which we use
      to update the last row.  The height of each row is dy pixels, which
      maps to DT seconds.  We call each dy high rectangle of pixels a
      block.

    */

    // determine dy, the height of one "block"
    px_per_sec = (float)m_rGrid.GetHeight() / WATERFALL_SECS_Y;
    dy = DT * px_per_sec;

    // number of dy high blocks in spectrogram
    dy_blocks = m_rGrid.GetHeight()/ dy;

    intensity_per_dB  = (float)256 /(MAX_MAG_DB - MIN_MAG_DB);
    spec_index_per_px = (float)FDMDV_NSPEC / (float) m_rGrid.GetWidth();

    /*
    printf("h %d w %d px_per_sec %d dy %d dy_blocks %d spec_index_per_px: %f\n",
	   m_rGrid.GetHeight(), m_rGrid.GetWidth(), px_per_sec,
	   dy, dy_blocks, spec_index_per_px);
    */

    // Shift previous bit map up one row of blocks ----------------------------

    wxNativePixelData data(*m_pBmp);
    assert(data != NULL);
    wxNativePixelData::Iterator bitMapStart(data);
    wxNativePixelData::Iterator p = bitMapStart;

    for(b = 0; b < dy_blocks - 1; b++) {
	wxNativePixelData::Iterator psrc = bitMapStart;
	wxNativePixelData::Iterator pdest = bitMapStart;
	pdest.OffsetY(data, dy * b);
	psrc.OffsetY(data, dy * (b+1));

	// copy one line of blocks

	for(py = 0; py < dy; py++) {
	    wxNativePixelData::Iterator pdestRowStart = pdest;
	    wxNativePixelData::Iterator psrcRowStart = psrc;

	    for(px = 0; px < m_rGrid.GetWidth(); px++) {
		pdest.Red() = psrc.Red();
		pdest.Green() = psrc.Green();
		pdest.Blue() = psrc.Blue();
		pdest++;
		psrc++;
	    }

	    pdest = pdestRowStart;
	    pdest.OffsetY(data, 1);
	    psrc = psrcRowStart;
	    psrc.OffsetY(data, 1);
        }

    }

    // Draw last line of blocks using latest amplitude data ------------------

    p = bitMapStart;
    p.OffsetY(data, dy *(dy_blocks - 1));

    for(py = 0; py < dy; py++)
    {
	wxNativePixelData::Iterator rowStart = p;

	for(px = 0; px < m_rGrid.GetWidth(); px++)
        {
	    index = px * spec_index_per_px;
	    assert(index < FDMDV_NSPEC);

	    intensity = intensity_per_dB * (g_avmag[index] - MIN_MAG_DB);
	    if(intensity > 255) intensity = 255;
	    if (intensity < 0) intensity = 0;
	    //printf("%d %f %d \n", index, g_avmag[index], intensity);

	    p.Red() = m_heatmap_lut[intensity] & 0xff;
	    p.Green() = (m_heatmap_lut[intensity] >> 8) & 0xff;
	    p.Blue() = (m_heatmap_lut[intensity] >> 16) & 0xff;
	    ++p;
        }

	p = rowStart;
	p.OffsetY(data, 1);
    }
}

//-------------------------------------------------------------------------
// OnMouseDown()
//-------------------------------------------------------------------------
void PlotWaterfall::OnMouseDown(wxMouseEvent& event)
{
    m_mouseDown = true;
    wxClientDC dc(this);

    wxPoint pt(event.GetLogicalPosition(dc));

    // map x coord to edges of actual plot
    pt.x -= PLOT_BORDER + XLEFT_OFFSET;
    pt.y -= PLOT_BORDER;

    // valid click if inside of plot
    if ((pt.x >= 0) && (pt.x <= m_rGrid.GetWidth()) && (pt.y >=0) && (pt.y < m_rGrid.GetHeight())) {
        float freq_hz_to_px = (float)m_rGrid.GetWidth()/(MAX_F_HZ-MIN_F_HZ);
        float clickFreq = (float)pt.x/freq_hz_to_px;

        // communicate back to other threads
        fdmdv2_clickTune(clickFreq);
    }
}
<commit_msg><commit_after>//==========================================================================
// Name:            fdmdv2_plot_waterfall.cpp
// Purpose:         Implements a waterfall plot derivative of fdmdv2_plot.
// Created:         June 22, 2012
// Authors:         David Rowe, David Witten
//
// License:
//
//  This program is free software; you can redistribute it and/or modify
//  it under the terms of the GNU General Public License version 2.1,
//  as published by the Free Software Foundation.  This program is
//  distributed in the hope that it will be useful, but WITHOUT ANY
//  WARRANTY; without even the implied warranty of MERCHANTABILITY or
//  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
//  License for more details.
//
//  You should have received a copy of the GNU General Public License
//  along with this program; if not, see <http://www.gnu.org/licenses/>.
//
//==========================================================================
#include <string.h>
#include "wx/wx.h"
#include "fdmdv2_main.h"

extern float g_avmag[];                 // av mag spec passed in to draw()
void fdmdv2_clickTune(float frequency); // callback to pass new click freq

BEGIN_EVENT_TABLE(PlotWaterfall, PlotPanel)
    EVT_PAINT           (PlotWaterfall::OnPaint)
    EVT_MOTION          (PlotWaterfall::OnMouseMove)
    EVT_LEFT_DOWN       (PlotWaterfall::OnMouseDown)
    EVT_LEFT_UP         (PlotWaterfall::OnMouseUp)
    EVT_MOUSEWHEEL      (PlotWaterfall::OnMouseWheelMoved)
    EVT_SIZE            (PlotWaterfall::OnSize)
    EVT_SHOW            (PlotWaterfall::OnShow)
END_EVENT_TABLE()

//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-=
// Class WaterfallPlot
//
// @class   WaterfallPlot
// @author  David Witten
// @date    $(Date)
// @file    $(CurrentFileName).$(CurrentFileExt)
// @brief
//
//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-=
PlotWaterfall::PlotWaterfall(wxFrame* parent): PlotPanel(parent)
{

    for(int i = 0; i < 255; i++)
    {
        m_heatmap_lut[i] = heatmap((float)i, 0.0, 255.0);
    }
    m_greyscale     = 0;
    m_Bufsz         = GetMaxClientSize();
    m_newdata       = false;
    m_firstPass     = true;
    m_line_color    = 0;
    SetLabelSize(10.0);

    m_pBmp = NULL;
}

// When the window size gets set we can work outthe size of the window
// we plot in and allocate a bit map of the correct size
void PlotWaterfall::OnSize(wxSizeEvent& event)
{
    // resize bit map

    delete m_pBmp;

    m_rCtrl  = GetClientRect();

    // m_rGrid is coords of inner window we actually plot to.  We deflate it a bit
    // to leave room for axis labels.

    m_rGrid  = m_rCtrl;
    m_rGrid = m_rGrid.Deflate(PLOT_BORDER + (XLEFT_OFFSET/2), (PLOT_BORDER + (YBOTTOM_OFFSET/2)));

    // we want a bit map the size of m_rGrid

    m_pBmp = new wxBitmap(m_rGrid.GetWidth(), m_rGrid.GetHeight(), 24);
}

//----------------------------------------------------------------
// paintEvent()
//
// @class $(Name)
// @author $(User)
// @date $(Date)
// @file $(CurrentFileName).$(CurrentFileExt)
// @brief
//
// Called by the system of by wxWidgets when the panel needs
// to be redrawn. You can also trigger this call by calling
// Refresh()/Update().
//----------------------------------------------------------------
void PlotWaterfall::OnPaint(wxPaintEvent & evt)
{
    wxAutoBufferedPaintDC dc(this);
    draw(dc);
}

//----------------------------------------------------------------
// OnShow()
//----------------------------------------------------------------
void PlotWaterfall::OnShow(wxShowEvent& event)
{
}

//----------------------------------------------------------------
// ~PlotWaterfall()
//----------------------------------------------------------------
PlotWaterfall::~PlotWaterfall()
{
}

//----------------------------------------------------------------
// heatmap()
// map val to a rgb colour
// from http://eddiema.ca/2011/01/21/c-sharp-heatmaps/
//----------------------------------------------------------------
unsigned PlotWaterfall::heatmap(float val, float min, float max)
{
    unsigned r = 0;
    unsigned g = 0;
    unsigned b = 0;

    val = (val - min) / (max - min);
    if(val <= 0.2)
    {
        b = (unsigned)((val / 0.2) * 255);
    }
    else if(val >  0.2 &&  val <= 0.7)
    {
        b = (unsigned)((1.0 - ((val - 0.2) / 0.5)) * 255);
    }
    if(val >= 0.2 &&  val <= 0.6)
    {
        g = (unsigned)(((val - 0.2) / 0.4) * 255);
    }
    else if(val >  0.6 &&  val <= 0.9)
    {
        g = (unsigned)((1.0 - ((val - 0.6) / 0.3)) * 255);
    }
    if(val >= 0.5)
    {
        r = (unsigned)(((val - 0.5) / 0.5) * 255);
    }
    //printf("%f %x %x %x\n", val, r, g, b);
    return  (b << 16) + (g << 8) + r;
}

//----------------------------------------------------------------
// draw()
//----------------------------------------------------------------
void PlotWaterfall::draw(wxAutoBufferedPaintDC& dc)
{
    m_rCtrl  = GetClientRect();

    // m_rGrid is coords of inner window we actually plot to.  We deflate it a bit
    // to leave room for axis labels.

    m_rGrid = m_rCtrl;
    m_rGrid = m_rGrid.Deflate(PLOT_BORDER + (XLEFT_OFFSET/2), (PLOT_BORDER + (YBOTTOM_OFFSET/2)));

    if (m_pBmp == NULL)
    {
        // we want a bit map the size of m_rGrid
        m_pBmp = new wxBitmap(m_rGrid.GetWidth(), m_rGrid.GetHeight(), 24);
    }

    dc.Clear();

    if(m_newdata)
    {
        m_newdata = false;
        plotPixelData();
        dc.DrawBitmap(*m_pBmp, PLOT_BORDER + XLEFT_OFFSET, PLOT_BORDER);
    }
    else
    {

        // no data to plot so just erase to black.  Blue looks nicer
        // but is same colour as low amplitude signal

        // Bug on Linux: When Stop is pressed this code doesn't erase
        // the lower 25% of the Waterfall Window

        m_rPlot = wxRect(PLOT_BORDER + XLEFT_OFFSET, PLOT_BORDER, m_rGrid.GetWidth(), m_rGrid.GetHeight());
        wxBrush ltGraphBkgBrush = wxBrush(BLACK_COLOR);
        dc.SetBrush(ltGraphBkgBrush);
        dc.SetPen(wxPen(BLACK_COLOR, 0));
        dc.DrawRectangle(m_rPlot);
    }
    drawGraticule(dc);
}

//-------------------------------------------------------------------------
// drawGraticule()
//-------------------------------------------------------------------------
void PlotWaterfall::drawGraticule(wxAutoBufferedPaintDC& dc)
{
    int      x, y, text_w, text_h;
    char     buf[15];
    wxString s;
    float    f, time, freq_hz_to_px, time_s_to_py;

    wxBrush ltGraphBkgBrush;
    ltGraphBkgBrush.SetStyle(wxBRUSHSTYLE_TRANSPARENT);
    ltGraphBkgBrush.SetColour(*wxBLACK);
    dc.SetBrush(ltGraphBkgBrush);
    dc.SetPen(wxPen(BLACK_COLOR, 1));

    freq_hz_to_px = (float)m_rGrid.GetWidth()/(MAX_F_HZ-MIN_F_HZ);
    time_s_to_py = (float)m_rGrid.GetHeight()/WATERFALL_SECS_Y;

    // upper LH coords of plot area are (PLOT_BORDER + XLEFT_OFFSET, PLOT_BORDER)
    // lower RH coords of plot area are (PLOT_BORDER + XLEFT_OFFSET + m_rGrid.GetWidth(),
    //                                   PLOT_BORDER + m_rGrid.GetHeight())
    // Vertical gridlines
    dc.SetPen(m_penShortDash);
    for(f=STEP_F_HZ; f<MAX_F_HZ; f+=STEP_F_HZ)
    {
        x = f*freq_hz_to_px;
        x += PLOT_BORDER + XLEFT_OFFSET;
            dc.DrawLine(x, m_rGrid.GetHeight() + PLOT_BORDER, x, PLOT_BORDER);
            sprintf(buf, "%4.0fHz", f);
        GetTextExtent(buf, &text_w, &text_h);
            dc.DrawText(buf, x - text_w/2, m_rGrid.GetHeight() + PLOT_BORDER + YBOTTOM_TEXT_OFFSET);
    }

    // Horizontal gridlines
    dc.SetPen(m_penDotDash);
    for(time=0; time<=WATERFALL_SECS_Y; time+=WATERFALL_SECS_STEP) {
	y = m_rGrid.GetHeight() - time*time_s_to_py;
	y += PLOT_BORDER;
	dc.DrawLine(PLOT_BORDER + XLEFT_OFFSET, y,
		    (m_rGrid.GetWidth() + PLOT_BORDER + XLEFT_OFFSET), y);
        sprintf(buf, "%3.0fs", time);
	GetTextExtent(buf, &text_w, &text_h);
        dc.DrawText(buf, PLOT_BORDER + XLEFT_OFFSET - text_w - XLEFT_TEXT_OFFSET, y-text_h/2);
   }

}

//-------------------------------------------------------------------------
// plotPixelData()
//-------------------------------------------------------------------------
void PlotWaterfall::plotPixelData()
{
    float       spec_index_per_px;
    float       intensity_per_dB;
    float       px_per_sec;
    int         index;
    int         dy;
    int         dy_blocks;
    int         b;
    int         px;
    int         py;
    int         intensity;

    /*
      Design Notes:

      The height in pixels represents WATERFALL_SECS_Y of data.  Every DT
      seconds we get a vector of FDMDV_NSPEC spectrum samples which we use
      to update the last row.  The height of each row is dy pixels, which
      maps to DT seconds.  We call each dy high rectangle of pixels a
      block.

    */

    // determine dy, the height of one "block"
    px_per_sec = (float)m_rGrid.GetHeight() / WATERFALL_SECS_Y;
    dy = DT * px_per_sec;

    // number of dy high blocks in spectrogram
    dy_blocks = m_rGrid.GetHeight()/ dy;

    intensity_per_dB  = (float)256 /(MAX_MAG_DB - MIN_MAG_DB);
    spec_index_per_px = (float)FDMDV_NSPEC / (float) m_rGrid.GetWidth();

    /*
    printf("h %d w %d px_per_sec %d dy %d dy_blocks %d spec_index_per_px: %f\n",
       m_rGrid.GetHeight(), m_rGrid.GetWidth(), px_per_sec,
       dy, dy_blocks, spec_index_per_px);
    */

    // Shift previous bit map up one row of blocks ----------------------------

    wxNativePixelData data(*m_pBmp);
    assert(data != NULL);
    wxNativePixelData::Iterator bitMapStart(data);
    wxNativePixelData::Iterator p = bitMapStart;

    for(b = 0; b < dy_blocks - 1; b++)
    {
        wxNativePixelData::Iterator psrc = bitMapStart;
        wxNativePixelData::Iterator pdest = bitMapStart;
        pdest.OffsetY(data, dy * b);
        psrc.OffsetY(data, dy * (b+1));

        // copy one line of blocks

        for(py = 0; py < dy; py++)
        {
            wxNativePixelData::Iterator pdestRowStart = pdest;
            wxNativePixelData::Iterator psrcRowStart = psrc;

            for(px = 0; px < m_rGrid.GetWidth(); px++)
            {
                pdest.Red() = psrc.Red();
                pdest.Green() = psrc.Green();
                pdest.Blue() = psrc.Blue();
                pdest++;
                psrc++;
            }
            pdest = pdestRowStart;
            pdest.OffsetY(data, 1);
            psrc = psrcRowStart;
            psrc.OffsetY(data, 1);
        }
    }

    // Draw last line of blocks using latest amplitude data ------------------
    p = bitMapStart;
    p.OffsetY(data, dy *(dy_blocks - 1));
    for(py = 0; py < dy; py++)
    {
        wxNativePixelData::Iterator rowStart = p;

        for(px = 0; px < m_rGrid.GetWidth(); px++)
        {
            index = px * spec_index_per_px;
            assert(index < FDMDV_NSPEC);

            intensity = intensity_per_dB * (g_avmag[index] - MIN_MAG_DB);
            if(intensity > 255) intensity = 255;
            if (intensity < 0) intensity = 0;
            //printf("%d %f %d \n", index, g_avmag[index], intensity);

            p.Red() = m_heatmap_lut[intensity] & 0xff;
            p.Green() = (m_heatmap_lut[intensity] >> 8) & 0xff;
            p.Blue() = (m_heatmap_lut[intensity] >> 16) & 0xff;
            ++p;
        }
        p = rowStart;
        p.OffsetY(data, 1);
    }
}

//-------------------------------------------------------------------------
// OnMouseDown()
//-------------------------------------------------------------------------
void PlotWaterfall::OnMouseDown(wxMouseEvent& event)
{
    m_mouseDown = true;
    wxClientDC dc(this);

    wxPoint pt(event.GetLogicalPosition(dc));

    // map x coord to edges of actual plot
    pt.x -= PLOT_BORDER + XLEFT_OFFSET;
    pt.y -= PLOT_BORDER;

    // valid click if inside of plot
    if ((pt.x >= 0) && (pt.x <= m_rGrid.GetWidth()) && (pt.y >=0) && (pt.y < m_rGrid.GetHeight()))
    {
        float freq_hz_to_px = (float)m_rGrid.GetWidth()/(MAX_F_HZ-MIN_F_HZ);
        float clickFreq = (float)pt.x/freq_hz_to_px;

        // communicate back to other threads
        fdmdv2_clickTune(clickFreq);
    }
}
<|endoftext|>


<commit_before>#include "VectorField.h"

#include <NQVTK/ParamSets/VolumeParamSet.h>

#include <NQVTK/Rendering/Scene.h>
#include <NQVTK/Rendering/ImageDataVolume.h>

#include <NQVTK/Renderables/PolyData.h>

#include <vtkCubeSource.h>
#include <vtkImageData.h>
#include <vtkMetaImageReader.h>
#include <vtkPolyData.h>
#include <vtkSmartPointer.h>
#include <vtkTriangleFilter.h>
#include <vtkXMLImageDataReader.h>

#include <QFileInfo>

namespace VFE
{
	// ------------------------------------------------------------------------
	// VTK Helpers
	vtkImageData *LoadVolume(const QString &filename)
	{
		vtkImageData *volume = 0;
		QFileInfo fi(filename);
		// TODO: handle errors gracefully
		if (fi.suffix() == "mha" || fi.suffix() == "mhd")
		{
			vtkSmartPointer<vtkMetaImageReader> reader =
				vtkSmartPointer<vtkMetaImageReader>::New();
			reader->SetFileName(filename.toUtf8());
			reader->Update();
			volume = reader->GetOutput();
			volume->Register(0);
		}
		else if (fi.suffix() == "vti")
		{
			vtkSmartPointer<vtkXMLImageDataReader> reader =
				vtkSmartPointer<vtkXMLImageDataReader>::New();
			reader->SetFileName(filename.toUtf8());
			reader->Update();
			volume = reader->GetOutput();
			volume->Register(0);
		}
		else
		{
			qDebug("Unsupported file type!");
		}

		// Quick sanity check
		bool ok = volume != 0 &&
			(volume->GetDataDimension() == 3) &&
			(volume->GetNumberOfScalarComponents() == 3);
		if (!ok)
		{
			qDebug("Not a vector volume!");
			volume->Delete();
			volume = 0;
		}

		return volume;
	}

	// ------------------------------------------------------------------------
	vtkPolyData *CreateBoundingBox(vtkImageData *volume)
	{
		vtkSmartPointer<vtkCubeSource> cube =
			vtkSmartPointer<vtkCubeSource>::New();
		cube->SetBounds(volume->GetBounds());

		// Triangulate, NQVTK doesn't like quads
		vtkSmartPointer<vtkTriangleFilter> tri =
			vtkSmartPointer<vtkTriangleFilter>::New();
		tri->SetInputConnection(cube->GetOutputPort());
		tri->Update();

		vtkPolyData *bb = tri->GetOutput();
		bb->Register(0);

		return bb;
	}

	// ------------------------------------------------------------------------
	VectorField::VectorField(NQVTK::Scene *scene) : scene(scene)
	{
	}

	// ------------------------------------------------------------------------
	VectorField::~VectorField()
	{
		delete scene;
	}

	// ------------------------------------------------------------------------
	VectorField *VectorField::Load(const QString &filename)
	{
		// Load the vector field
		vtkImageData *vtkvolume = LoadVolume(filename);
		if (!vtkvolume)
		{
			// Returns null when errors occur
			return 0;
		}

		// Create bounding geometry
		vtkPolyData *vtkbb = CreateBoundingBox(vtkvolume);

		// Assemble NQVTK scene
		NQVTK::Scene *scene = new NQVTK::Scene();
		NQVTK::Renderable *bb = new NQVTK::PolyData(vtkbb);
		NQVTK::Volume *volume = NQVTK::ImageDataVolume::New(vtkvolume);
		bb->SetParamSet("volume", new NQVTK::VolumeParamSet(volume));
		scene->AddRenderable(bb);

		// Delete VTK data
		vtkvolume->Delete();
		vtkbb->Delete();

		// Create the VectorField instance
		VectorField *vf = new VectorField(scene);

		return vf;
	}
}
<commit_msg>Set bounding box opacity so the shader doesn't have to.<commit_after>#include "VectorField.h"

#include <NQVTK/ParamSets/VolumeParamSet.h>

#include <NQVTK/Rendering/Scene.h>
#include <NQVTK/Rendering/ImageDataVolume.h>

#include <NQVTK/Renderables/PolyData.h>

#include <vtkCubeSource.h>
#include <vtkImageData.h>
#include <vtkMetaImageReader.h>
#include <vtkPolyData.h>
#include <vtkSmartPointer.h>
#include <vtkTriangleFilter.h>
#include <vtkXMLImageDataReader.h>

#include <QFileInfo>

namespace VFE
{
	// ------------------------------------------------------------------------
	// VTK Helpers
	vtkImageData *LoadVolume(const QString &filename)
	{
		vtkImageData *volume = 0;
		QFileInfo fi(filename);
		// TODO: handle errors gracefully
		if (fi.suffix() == "mha" || fi.suffix() == "mhd")
		{
			vtkSmartPointer<vtkMetaImageReader> reader =
				vtkSmartPointer<vtkMetaImageReader>::New();
			reader->SetFileName(filename.toUtf8());
			reader->Update();
			volume = reader->GetOutput();
			volume->Register(0);
		}
		else if (fi.suffix() == "vti")
		{
			vtkSmartPointer<vtkXMLImageDataReader> reader =
				vtkSmartPointer<vtkXMLImageDataReader>::New();
			reader->SetFileName(filename.toUtf8());
			reader->Update();
			volume = reader->GetOutput();
			volume->Register(0);
		}
		else
		{
			qDebug("Unsupported file type!");
		}

		// Quick sanity check
		bool ok = volume != 0 &&
			(volume->GetDataDimension() == 3) &&
			(volume->GetNumberOfScalarComponents() == 3);
		if (!ok)
		{
			qDebug("Not a vector volume!");
			volume->Delete();
			volume = 0;
		}

		return volume;
	}

	// ------------------------------------------------------------------------
	vtkPolyData *CreateBoundingBox(vtkImageData *volume)
	{
		vtkSmartPointer<vtkCubeSource> cube =
			vtkSmartPointer<vtkCubeSource>::New();
		cube->SetBounds(volume->GetBounds());

		// Triangulate, NQVTK doesn't like quads
		vtkSmartPointer<vtkTriangleFilter> tri =
			vtkSmartPointer<vtkTriangleFilter>::New();
		tri->SetInputConnection(cube->GetOutputPort());
		tri->Update();

		vtkPolyData *bb = tri->GetOutput();
		bb->Register(0);

		return bb;
	}

	// ------------------------------------------------------------------------
	VectorField::VectorField(NQVTK::Scene *scene) : scene(scene)
	{
	}

	// ------------------------------------------------------------------------
	VectorField::~VectorField()
	{
		delete scene;
	}

	// ------------------------------------------------------------------------
	VectorField *VectorField::Load(const QString &filename)
	{
		// Load the vector field
		vtkImageData *vtkvolume = LoadVolume(filename);
		if (!vtkvolume)
		{
			// Returns null when errors occur
			return 0;
		}

		// Create bounding geometry
		vtkPolyData *vtkbb = CreateBoundingBox(vtkvolume);

		// Assemble NQVTK scene
		NQVTK::Scene *scene = new NQVTK::Scene();
		NQVTK::Renderable *bb = new NQVTK::PolyData(vtkbb);
		NQVTK::Volume *volume = NQVTK::ImageDataVolume::New(vtkvolume);
		bb->SetParamSet("volume", new NQVTK::VolumeParamSet(volume));
		bb->opacity = 0.1;
		scene->AddRenderable(bb);

		// Delete VTK data
		vtkvolume->Delete();
		vtkbb->Delete();

		// Create the VectorField instance
		VectorField *vf = new VectorField(scene);

		return vf;
	}
}
<|endoftext|>


<commit_before>#if TZ_VULKAN
#include "core/profiling/zone.hpp"
#include "gl/impl/frontend/vk2/renderer.hpp"
#include "gl/impl/frontend/vk2/device.hpp"
#include "gl/impl/frontend/vk2/convert.hpp"
#include "gl/impl/backend/vk2/extensions.hpp"
#include "gl/impl/backend/vk2/hardware/physical_device.hpp"
#include "gl/impl/backend/vk2/image_format.hpp"
#include <variant>

namespace tz::gl
{
	DeviceWindowVulkan::DeviceWindowVulkan(const vk2::LogicalDevice& device):
	DeviceWindowVulkan()
	{
		TZ_PROFZONE("DeviceWindowVulkan Create", TZ_PROFCOL_YELLOW);
		const vk2::VulkanInstance& instance = device.get_hardware().get_instance();
		tz_assert(instance.has_surface(), "DeviceWindowVulkan provided a VulkanInstance which is not headless, but doesn't have a WindowSurface attached. Please submit a bug report.");

		// Create a swapchain.
		// Ideally we want mailbox present mode, but that may not be available.
		vk2::SurfacePresentMode present_mode = vk2::SurfacePresentMode::Mailbox;
		if(!device.get_hardware().get_supported_surface_present_modes().contains(present_mode))
		{
			present_mode = device.get_hardware().get_supported_surface_present_modes().front();
		}
		std::uint32_t swapchain_img_min = device.get_hardware().get_surface_capabilities().min_image_count;
		std::uint32_t swapchain_img_max = device.get_hardware().get_surface_capabilities().max_image_count;
		std::uint32_t swapchain_img_count = std::clamp<std::uint32_t>(3u, swapchain_img_min, swapchain_img_max);
		swapchain_img_count = std::min<std::uint32_t>(swapchain_img_count, 3u);
		this->window_buf = vk2::Swapchain
		{{
			.device = &device,
			.swapchain_image_count_minimum = swapchain_img_count,
			.image_format = device.get_hardware().get_supported_surface_formats().front(),
			.present_mode = present_mode
		}};
		this->register_resize();
	}

	DeviceWindowVulkan::DeviceWindowVulkan(DeviceWindowVulkan&& move):
	window_buf(std::monostate{})
	{
		*this = std::move(move);
	}

	DeviceWindowVulkan::~DeviceWindowVulkan()
	{
		this->unregister_resize();
	}

	DeviceWindowVulkan& DeviceWindowVulkan::operator=(DeviceWindowVulkan&& rhs)
	{
		std::swap(this->window_buf, rhs.window_buf);
		std::swap(this->on_resize_handle, rhs.on_resize_handle);
		this->reregister_resize();
		rhs.unregister_resize();
		return *this;
	}

	bool DeviceWindowVulkan::valid() const
	{
		return !std::holds_alternative<std::monostate>(this->window_buf);
	}

	vk2::Swapchain* DeviceWindowVulkan::as_swapchain()
	{
		if(std::holds_alternative<vk2::Swapchain>(window_buf))
		{
			return &std::get<vk2::Swapchain>(window_buf);
		}
		return nullptr;
	}

	const vk2::Swapchain* DeviceWindowVulkan::as_swapchain() const
	{
		if(std::holds_alternative<vk2::Swapchain>(window_buf))
		{
			return &std::get<vk2::Swapchain>(window_buf);
		}
		return nullptr;
	}

	vk2::Image* DeviceWindowVulkan::as_image()
	{
		if(std::holds_alternative<vk2::Image>(window_buf))
		{
			return &std::get<vk2::Image>(window_buf);
		}
		return nullptr;
	}

	const vk2::Image* DeviceWindowVulkan::as_image() const
	{
		if(std::holds_alternative<vk2::Image>(window_buf))
		{
			return &std::get<vk2::Image>(window_buf);
		}
		return nullptr;
	}

	tz::Vec2ui DeviceWindowVulkan::get_dimensions() const
	{
		if(this->as_swapchain() != nullptr)
		{
			return this->as_swapchain()->get_dimensions();
		}
		else if(this->as_image() != nullptr)
		{
			return this->as_image()->get_dimensions();
		}
		return {0u, 0u};
	}

	vk2::ImageFormat DeviceWindowVulkan::get_format() const
	{
		if(this->as_swapchain() != nullptr)
		{
			return this->as_swapchain()->get_image_format();
		}
		else if(this->as_image() != nullptr)
		{
			return this->as_image()->get_format();
		}
		return vk2::ImageFormat::Undefined;
	}

	RendererResizeCallbackType& DeviceWindowVulkan::resize_callback()
	{
		return this->renderer_resize_callbacks;
	}

	std::span<vk2::Image> DeviceWindowVulkan::get_output_images()
	{
		if(this->as_swapchain() != nullptr)
		{
			return this->as_swapchain()->get_images();
		}
		else if(this->as_image() != nullptr)
		{
			return {this->as_image(), 1};
		}
		tz_error("Could not recognise window storage. Please submit a bug report.");
		return {};
	}

	void DeviceWindowVulkan::on_resize(tz::Vec2ui dims)
	{
		TZ_PROFZONE("DeviceWindowVulkan OnResize", TZ_PROFCOL_YELLOW);
		if(dims[0] == 0 || dims[0] == 0)
		{
			return;
		}
		// Assume we have a head, headless no support for resizeable output yet.
		tz_assert(this->as_swapchain() != nullptr, "Resizeable output for headless applications is not yet supported.");
		vk2::Swapchain& old_swapchain = *this->as_swapchain();
		if(old_swapchain.get_dimensions() == dims)
		{
			// Dimensions didn't actually change. Early out and don't bother telling the renderers.
			return;
		}
		vk2::Swapchain new_swapchain
		{{
			.device = &old_swapchain.get_device(),
			.swapchain_image_count_minimum = static_cast<std::uint32_t>(old_swapchain.get_image_views().size()),
			.image_format = old_swapchain.get_image_format(),
			.present_mode = old_swapchain.get_present_mode(),
			.old_swapchain = &old_swapchain
		}};
		std::swap(old_swapchain, new_swapchain);
		// Now notify all renderers.
		this->renderer_resize_callbacks
		({
			.new_dimensions = dims,
			.new_output_images = this->get_output_images()
		});
	}

	void DeviceWindowVulkan::register_resize()
	{
		this->on_resize_handle = tz::window().on_resize().add_callback([this](tz::Vec2ui dims){this->on_resize(dims);});
	}

	void DeviceWindowVulkan::unregister_resize()
	{
		if(this->is_resize_registered())
		{
			tz::window().on_resize().remove_callback(this->on_resize_handle);
			this->on_resize_handle = tz::nullhand;
		}
	}

	bool DeviceWindowVulkan::is_resize_registered() const
	{
		return this->on_resize_handle != tz::nullhand;
	}

	void DeviceWindowVulkan::reregister_resize()
	{
		this->unregister_resize();
		this->register_resize();
	}

//--------------------------------------------------------------------------------------------------

	DeviceVulkan::DeviceVulkan():
	DeviceVulkan(vk2::get())
	{

	}

	DeviceVulkan::DeviceVulkan(const vk2::VulkanInstance& instance):
	device(vk2::LogicalDevice::null()),
	window_storage()
	{
		TZ_PROFZONE("Vulkan Frontend - DeviceVulkan Create", TZ_PROFCOL_YELLOW);
		// First, create a LogicalDevice.
		// TODO: Don't just choose a device at random.
		vk2::PhysicalDevice pdev = vk2::get_all_devices(instance).front();

		vk2::DeviceExtensionList dev_exts = {};
		const vk2::DeviceFeatureField dev_feats =
		{
			vk2::DeviceFeature::BindlessDescriptors,
			vk2::DeviceFeature::MultiDrawIndirect,
			vk2::DeviceFeature::ColourBlendLogicalOperations
		};
		tz_assert(pdev.get_supported_features().contains(dev_feats), "One or both of DeviceFeatures 'BindlessDescriptors' and 'MultiDrawIndirect' are not supported by this machine/driver. Please ensure your machine meets the system requirements.");
		dev_exts = {vk2::DeviceExtension::Swapchain};
		#if TZ_DEBUG
			dev_exts |= vk2::DeviceExtension::ShaderDebugPrint;
		#endif
		tz_assert(pdev.get_supported_extensions().contains(dev_exts), "One or more of the %zu required DeviceExtensions are not supported by this machine/driver. Please ensure your machine meets the system requirements.", dev_exts.count());
		this->device =
		{{
			.physical_device = pdev,
			.extensions = dev_exts,
			.features = dev_feats
		}};

		// Now finally create the DeviceWindow.
		this->window_storage = {this->device};
	}

	RendererVulkan DeviceVulkan::create_renderer(const RendererInfoVulkan& info)
	{
		TZ_PROFZONE("Vulkan Frontend - Renderer Create (via DeviceVulkan)", TZ_PROFCOL_YELLOW);
		return {info,
		{
			.device = &this->device,
			.output_images = this->window_storage.get_output_images(),
			.maybe_swapchain = this->window_storage.as_swapchain(),
			.resize_callback = &this->window_storage.resize_callback()
		}};
	}

	ImageFormat DeviceVulkan::get_window_format() const
	{
		return from_vk2(this->window_storage.get_format());
	}

}

#endif // TZ_VULKAN
<commit_msg>- DeviceVulkan no longer uses vk2::DeviceFeature::MultiDrawIndirect because MDI is unused.<commit_after>#if TZ_VULKAN
#include "core/profiling/zone.hpp"
#include "gl/impl/frontend/vk2/renderer.hpp"
#include "gl/impl/frontend/vk2/device.hpp"
#include "gl/impl/frontend/vk2/convert.hpp"
#include "gl/impl/backend/vk2/extensions.hpp"
#include "gl/impl/backend/vk2/hardware/physical_device.hpp"
#include "gl/impl/backend/vk2/image_format.hpp"
#include <variant>

namespace tz::gl
{
	DeviceWindowVulkan::DeviceWindowVulkan(const vk2::LogicalDevice& device):
	DeviceWindowVulkan()
	{
		TZ_PROFZONE("DeviceWindowVulkan Create", TZ_PROFCOL_YELLOW);
		const vk2::VulkanInstance& instance = device.get_hardware().get_instance();
		tz_assert(instance.has_surface(), "DeviceWindowVulkan provided a VulkanInstance which is not headless, but doesn't have a WindowSurface attached. Please submit a bug report.");

		// Create a swapchain.
		// Ideally we want mailbox present mode, but that may not be available.
		vk2::SurfacePresentMode present_mode = vk2::SurfacePresentMode::Mailbox;
		if(!device.get_hardware().get_supported_surface_present_modes().contains(present_mode))
		{
			present_mode = device.get_hardware().get_supported_surface_present_modes().front();
		}
		std::uint32_t swapchain_img_min = device.get_hardware().get_surface_capabilities().min_image_count;
		std::uint32_t swapchain_img_max = device.get_hardware().get_surface_capabilities().max_image_count;
		std::uint32_t swapchain_img_count = std::clamp<std::uint32_t>(3u, swapchain_img_min, swapchain_img_max);
		swapchain_img_count = std::min<std::uint32_t>(swapchain_img_count, 3u);
		this->window_buf = vk2::Swapchain
		{{
			.device = &device,
			.swapchain_image_count_minimum = swapchain_img_count,
			.image_format = device.get_hardware().get_supported_surface_formats().front(),
			.present_mode = present_mode
		}};
		this->register_resize();
	}

	DeviceWindowVulkan::DeviceWindowVulkan(DeviceWindowVulkan&& move):
	window_buf(std::monostate{})
	{
		*this = std::move(move);
	}

	DeviceWindowVulkan::~DeviceWindowVulkan()
	{
		this->unregister_resize();
	}

	DeviceWindowVulkan& DeviceWindowVulkan::operator=(DeviceWindowVulkan&& rhs)
	{
		std::swap(this->window_buf, rhs.window_buf);
		std::swap(this->on_resize_handle, rhs.on_resize_handle);
		this->reregister_resize();
		rhs.unregister_resize();
		return *this;
	}

	bool DeviceWindowVulkan::valid() const
	{
		return !std::holds_alternative<std::monostate>(this->window_buf);
	}

	vk2::Swapchain* DeviceWindowVulkan::as_swapchain()
	{
		if(std::holds_alternative<vk2::Swapchain>(window_buf))
		{
			return &std::get<vk2::Swapchain>(window_buf);
		}
		return nullptr;
	}

	const vk2::Swapchain* DeviceWindowVulkan::as_swapchain() const
	{
		if(std::holds_alternative<vk2::Swapchain>(window_buf))
		{
			return &std::get<vk2::Swapchain>(window_buf);
		}
		return nullptr;
	}

	vk2::Image* DeviceWindowVulkan::as_image()
	{
		if(std::holds_alternative<vk2::Image>(window_buf))
		{
			return &std::get<vk2::Image>(window_buf);
		}
		return nullptr;
	}

	const vk2::Image* DeviceWindowVulkan::as_image() const
	{
		if(std::holds_alternative<vk2::Image>(window_buf))
		{
			return &std::get<vk2::Image>(window_buf);
		}
		return nullptr;
	}

	tz::Vec2ui DeviceWindowVulkan::get_dimensions() const
	{
		if(this->as_swapchain() != nullptr)
		{
			return this->as_swapchain()->get_dimensions();
		}
		else if(this->as_image() != nullptr)
		{
			return this->as_image()->get_dimensions();
		}
		return {0u, 0u};
	}

	vk2::ImageFormat DeviceWindowVulkan::get_format() const
	{
		if(this->as_swapchain() != nullptr)
		{
			return this->as_swapchain()->get_image_format();
		}
		else if(this->as_image() != nullptr)
		{
			return this->as_image()->get_format();
		}
		return vk2::ImageFormat::Undefined;
	}

	RendererResizeCallbackType& DeviceWindowVulkan::resize_callback()
	{
		return this->renderer_resize_callbacks;
	}

	std::span<vk2::Image> DeviceWindowVulkan::get_output_images()
	{
		if(this->as_swapchain() != nullptr)
		{
			return this->as_swapchain()->get_images();
		}
		else if(this->as_image() != nullptr)
		{
			return {this->as_image(), 1};
		}
		tz_error("Could not recognise window storage. Please submit a bug report.");
		return {};
	}

	void DeviceWindowVulkan::on_resize(tz::Vec2ui dims)
	{
		TZ_PROFZONE("DeviceWindowVulkan OnResize", TZ_PROFCOL_YELLOW);
		if(dims[0] == 0 || dims[0] == 0)
		{
			return;
		}
		// Assume we have a head, headless no support for resizeable output yet.
		tz_assert(this->as_swapchain() != nullptr, "Resizeable output for headless applications is not yet supported.");
		vk2::Swapchain& old_swapchain = *this->as_swapchain();
		if(old_swapchain.get_dimensions() == dims)
		{
			// Dimensions didn't actually change. Early out and don't bother telling the renderers.
			return;
		}
		vk2::Swapchain new_swapchain
		{{
			.device = &old_swapchain.get_device(),
			.swapchain_image_count_minimum = static_cast<std::uint32_t>(old_swapchain.get_image_views().size()),
			.image_format = old_swapchain.get_image_format(),
			.present_mode = old_swapchain.get_present_mode(),
			.old_swapchain = &old_swapchain
		}};
		std::swap(old_swapchain, new_swapchain);
		// Now notify all renderers.
		this->renderer_resize_callbacks
		({
			.new_dimensions = dims,
			.new_output_images = this->get_output_images()
		});
	}

	void DeviceWindowVulkan::register_resize()
	{
		this->on_resize_handle = tz::window().on_resize().add_callback([this](tz::Vec2ui dims){this->on_resize(dims);});
	}

	void DeviceWindowVulkan::unregister_resize()
	{
		if(this->is_resize_registered())
		{
			tz::window().on_resize().remove_callback(this->on_resize_handle);
			this->on_resize_handle = tz::nullhand;
		}
	}

	bool DeviceWindowVulkan::is_resize_registered() const
	{
		return this->on_resize_handle != tz::nullhand;
	}

	void DeviceWindowVulkan::reregister_resize()
	{
		this->unregister_resize();
		this->register_resize();
	}

//--------------------------------------------------------------------------------------------------

	DeviceVulkan::DeviceVulkan():
	DeviceVulkan(vk2::get())
	{

	}

	DeviceVulkan::DeviceVulkan(const vk2::VulkanInstance& instance):
	device(vk2::LogicalDevice::null()),
	window_storage()
	{
		TZ_PROFZONE("Vulkan Frontend - DeviceVulkan Create", TZ_PROFCOL_YELLOW);
		// First, create a LogicalDevice.
		// TODO: Don't just choose a device at random.
		vk2::PhysicalDevice pdev = vk2::get_all_devices(instance).front();

		vk2::DeviceExtensionList dev_exts = {};
		const vk2::DeviceFeatureField dev_feats =
		{
			vk2::DeviceFeature::BindlessDescriptors,
			vk2::DeviceFeature::ColourBlendLogicalOperations
		};
		tz_assert(pdev.get_supported_features().contains(dev_feats), "One or both of DeviceFeatures 'BindlessDescriptors' and 'ColourBlendLogicalOperations' are not supported by this machine/driver. Please ensure your machine meets the system requirements.");
		dev_exts = {vk2::DeviceExtension::Swapchain};
		#if TZ_DEBUG
			dev_exts |= vk2::DeviceExtension::ShaderDebugPrint;
		#endif
		tz_assert(pdev.get_supported_extensions().contains(dev_exts), "One or more of the %zu required DeviceExtensions are not supported by this machine/driver. Please ensure your machine meets the system requirements.", dev_exts.count());
		this->device =
		{{
			.physical_device = pdev,
			.extensions = dev_exts,
			.features = dev_feats
		}};

		// Now finally create the DeviceWindow.
		this->window_storage = {this->device};
	}

	RendererVulkan DeviceVulkan::create_renderer(const RendererInfoVulkan& info)
	{
		TZ_PROFZONE("Vulkan Frontend - Renderer Create (via DeviceVulkan)", TZ_PROFCOL_YELLOW);
		return {info,
		{
			.device = &this->device,
			.output_images = this->window_storage.get_output_images(),
			.maybe_swapchain = this->window_storage.as_swapchain(),
			.resize_callback = &this->window_storage.resize_callback()
		}};
	}

	ImageFormat DeviceVulkan::get_window_format() const
	{
		return from_vk2(this->window_storage.get_format());
	}

}

#endif // TZ_VULKAN
<|endoftext|>


<commit_before>
/*
 * Copyright 2011 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#include <GL/osmesa.h>

#include "gl/SkMesaGLContext.h"

SkMesaGLContext::AutoContextRestore::AutoContextRestore() {
    fOldContext = (Context)OSMesaGetCurrentContext();
    if (NULL != (OSMesaContext)fOldContext) {
        OSMesaGetColorBuffer((OSMesaContext)fOldContext,
                              &fOldWidth, &fOldHeight,
                              &fOldFormat, &fOldImage);
    }
}

SkMesaGLContext::AutoContextRestore::~AutoContextRestore() {
    if (NULL != (OSMesaContext)fOldContext) {
        OSMesaMakeCurrent((OSMesaContext)fOldContext, fOldImage,
                          fOldFormat, fOldWidth, fOldHeight);
    }
}

///////////////////////////////////////////////////////////////////////////////

SkMesaGLContext::SkMesaGLContext()
    : fContext(NULL)
    , fImage(NULL) {
    GR_STATIC_ASSERT(sizeof(Context) == sizeof(OSMesaContext));
}

SkMesaGLContext::~SkMesaGLContext() {
    this->destroyGLContext();
}

void SkMesaGLContext::destroyGLContext() {
    if (fImage) {
        sk_free(fImage);
    }

    if (fContext) {
        OSMesaDestroyContext((OSMesaContext)fContext);
    }
}

static const GrGLint gBOGUS_SIZE = 16;

const GrGLInterface* SkMesaGLContext::createGLContext() {
    /* Create an RGBA-mode context */
#if OSMESA_MAJOR_VERSION * 100 + OSMESA_MINOR_VERSION >= 305
    /* specify Z, stencil, accum sizes */
    fContext = (Context)OSMesaCreateContextExt(OSMESA_BGRA, 0, 0, 0, NULL);
#else
    fContext = (Context)OSMesaCreateContext(OSMESA_BGRA, NULL);
#endif
    if (!fContext) {
        SkDebugf("OSMesaCreateContext failed!\n");
        this->destroyGLContext();
        return NULL;
    }
    // Allocate the image buffer
    fImage = (GrGLubyte *) sk_malloc_throw(gBOGUS_SIZE * gBOGUS_SIZE *
                                           4 * sizeof(GrGLubyte));
    if (!fImage) {
        SkDebugf("Alloc image buffer failed!\n");
        this->destroyGLContext();
        return NULL;
    }

    // Bind the buffer to the context and make it current
    if (!OSMesaMakeCurrent((OSMesaContext)fContext,
                           fImage,
                           GR_GL_UNSIGNED_BYTE,
                           gBOGUS_SIZE,
                           gBOGUS_SIZE)) {
        SkDebugf("OSMesaMakeCurrent failed!\n");
        this->destroyGLContext();
        return NULL;
    }

    const GrGLInterface* interface = GrGLCreateMesaInterface();
    if (!interface) {
        SkDebugf("Could not create GL interface!\n");
        this->destroyGLContext();
        return NULL;
    }
    return interface;

}

void SkMesaGLContext::makeCurrent() const {
    if (fContext) {
        if (!OSMesaMakeCurrent((OSMesaContext)fContext, fImage,
                               GR_GL_UNSIGNED_BYTE, gBOGUS_SIZE, gBOGUS_SIZE)) {
            SkDebugf("Could not make MESA context current.");
        }
    }
}
<commit_msg>Hopefully fixes linux + mac build breaks from r3851 by adding missing include in SkMesaGLContext.cpp.<commit_after>
/*
 * Copyright 2011 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#include <GL/osmesa.h>

#include "gl/SkMesaGLContext.h"
#include "gl/GrGLDefines.h"

SkMesaGLContext::AutoContextRestore::AutoContextRestore() {
    fOldContext = (Context)OSMesaGetCurrentContext();
    if (NULL != (OSMesaContext)fOldContext) {
        OSMesaGetColorBuffer((OSMesaContext)fOldContext,
                              &fOldWidth, &fOldHeight,
                              &fOldFormat, &fOldImage);
    }
}

SkMesaGLContext::AutoContextRestore::~AutoContextRestore() {
    if (NULL != (OSMesaContext)fOldContext) {
        OSMesaMakeCurrent((OSMesaContext)fOldContext, fOldImage,
                          fOldFormat, fOldWidth, fOldHeight);
    }
}

///////////////////////////////////////////////////////////////////////////////

SkMesaGLContext::SkMesaGLContext()
    : fContext(NULL)
    , fImage(NULL) {
    GR_STATIC_ASSERT(sizeof(Context) == sizeof(OSMesaContext));
}

SkMesaGLContext::~SkMesaGLContext() {
    this->destroyGLContext();
}

void SkMesaGLContext::destroyGLContext() {
    if (fImage) {
        sk_free(fImage);
    }

    if (fContext) {
        OSMesaDestroyContext((OSMesaContext)fContext);
    }
}

static const GrGLint gBOGUS_SIZE = 16;

const GrGLInterface* SkMesaGLContext::createGLContext() {
    /* Create an RGBA-mode context */
#if OSMESA_MAJOR_VERSION * 100 + OSMESA_MINOR_VERSION >= 305
    /* specify Z, stencil, accum sizes */
    fContext = (Context)OSMesaCreateContextExt(OSMESA_BGRA, 0, 0, 0, NULL);
#else
    fContext = (Context)OSMesaCreateContext(OSMESA_BGRA, NULL);
#endif
    if (!fContext) {
        SkDebugf("OSMesaCreateContext failed!\n");
        this->destroyGLContext();
        return NULL;
    }
    // Allocate the image buffer
    fImage = (GrGLubyte *) sk_malloc_throw(gBOGUS_SIZE * gBOGUS_SIZE *
                                           4 * sizeof(GrGLubyte));
    if (!fImage) {
        SkDebugf("Alloc image buffer failed!\n");
        this->destroyGLContext();
        return NULL;
    }

    // Bind the buffer to the context and make it current
    if (!OSMesaMakeCurrent((OSMesaContext)fContext,
                           fImage,
                           GR_GL_UNSIGNED_BYTE,
                           gBOGUS_SIZE,
                           gBOGUS_SIZE)) {
        SkDebugf("OSMesaMakeCurrent failed!\n");
        this->destroyGLContext();
        return NULL;
    }

    const GrGLInterface* interface = GrGLCreateMesaInterface();
    if (!interface) {
        SkDebugf("Could not create GL interface!\n");
        this->destroyGLContext();
        return NULL;
    }
    return interface;

}

void SkMesaGLContext::makeCurrent() const {
    if (fContext) {
        if (!OSMesaMakeCurrent((OSMesaContext)fContext, fImage,
                               GR_GL_UNSIGNED_BYTE, gBOGUS_SIZE, gBOGUS_SIZE)) {
            SkDebugf("Could not make MESA context current.");
        }
    }
}
<|endoftext|>


<commit_before>/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: Nokia Corporation (info@qt.nokia.com)
**
**
** GNU Lesser General Public License Usage
**
** This file may be used under the terms of the GNU Lesser General Public
** License version 2.1 as published by the Free Software Foundation and
** appearing in the file LICENSE.LGPL included in the packaging of this file.
** Please review the following information to ensure the GNU Lesser General
** Public License version 2.1 requirements will be met:
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights. These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** Other Usage
**
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Nokia.
**
** If you have questions regarding the use of this file, please contact
** Nokia at info@qt.nokia.com.
**
**************************************************************************/

#include <QScopedPointer>
#include <QLatin1String>
#include <QGraphicsObject>
#include <QApplication>
#include <QSettings>
#include <QFileInfo>

#include <qmljs/qmljsinterpreter.h>
#include <qmljs/qmljsdocument.h>
#include <qmljs/qmljsbind.h>
#include <qmljs/qmljslookupcontext.h>
#include <qmljs/qmljscontext.h>
#include <qmljs/parser/qmljsast_p.h>
#include <qmljstools/qmljsrefactoringchanges.h>
#include <qmljstools/qmljsmodelmanager.h>
#include <qmldesigner/designercore/include/metainfo.h>
#include <extensionsystem/pluginmanager.h>

#include <QtTest>

using namespace QmlJS;
using namespace QmlJSTools;

class tst_Basic : public QObject
{
    Q_OBJECT
public:
    tst_Basic();

private slots:
    void initTestCase();
    void cleanupTestCase();

    void basicObjectTests();
    //void testMetaInfo();

private:
    ExtensionSystem::PluginManager *pluginManager;
};

tst_Basic::tst_Basic()
{
}

#ifdef Q_OS_MAC
#  define SHARE_PATH "/Resources"
#else
#  define SHARE_PATH "/share/qtcreator"
#endif

QString resourcePath()
{
    return QDir::cleanPath(QTCREATORDIR + QLatin1String(SHARE_PATH));
}
void tst_Basic::initTestCase()
{
    pluginManager = new ExtensionSystem::PluginManager;
    QSettings *settings = new QSettings(QSettings::IniFormat, QSettings::UserScope,
                                 QLatin1String("Nokia"), QLatin1String("QtCreator"));
    pluginManager->setSettings(settings);
    pluginManager->setFileExtension(QLatin1String("pluginspec"));
    pluginManager->setPluginPaths(QStringList() << QLatin1String(Q_PLUGIN_PATH));
    pluginManager->loadPlugins();

    // the resource path is wrong, have to load things manually
    QFileInfo builtins(resourcePath() + "/qml-type-descriptions/builtins.qmltypes");
    QStringList errors, warnings;
    Interpreter::CppQmlTypesLoader::defaultQtObjects = Interpreter::CppQmlTypesLoader::loadQmlTypes(QFileInfoList() << builtins, &errors, &warnings);
}

void tst_Basic::cleanupTestCase()
{
    pluginManager->shutdown();
    delete pluginManager;
}

void tst_Basic::basicObjectTests()
{
    QmlJS::ModelManagerInterface *modelManager = QmlJS::ModelManagerInterface::instance();

    QStringList files(QString(QTCREATORDIR) + "/tests/auto/qml/qmldesigner/data/fx/usingmybutton.qml");
    modelManager->updateSourceFiles(files, false);
    modelManager->joinAllThreads();

    Snapshot snapshot = modelManager->snapshot();
    Document::Ptr doc = snapshot.document(QString(QTCREATORDIR) + "/tests/auto/qml/qmldesigner/data/fx/usingmybutton.qml");
    QVERIFY(doc && doc->isQmlDocument());

    LookupContext::Ptr lookupContext = LookupContext::create(doc, snapshot, QList<AST::Node*>());
    QVERIFY(lookupContext);

    Interpreter::QmlObjectValue *rectangleValue = lookupContext->valueOwner()->cppQmlTypes().typeByQualifiedName(
                "Qt", "Rectangle", LanguageUtils::ComponentVersion(4, 7));
    QVERIFY(rectangleValue);
    QVERIFY(!rectangleValue->isWritable("border"));
    QVERIFY(rectangleValue->hasProperty("border"));
    QVERIFY(rectangleValue->isPointer("border"));
    QVERIFY(rectangleValue->isWritable("color"));
    QVERIFY(!rectangleValue->isPointer("color"));

    const Interpreter::ObjectValue *ovItem = lookupContext->context()->lookupType(doc.data(), QStringList() << "Item");
    QCOMPARE(ovItem->className(), QString("Item"));
    QCOMPARE(lookupContext->context()->imports(doc.data())->info("Item", lookupContext->context().data()).name(), QString("Qt"));
    const Interpreter::ObjectValue *ovButton = lookupContext->context()->lookupType(doc.data(), QStringList() << "MyButton");
    QCOMPARE(ovButton->className(), QString("MyButton"));
    QCOMPARE(ovButton->prototype(lookupContext->context())->className(), QString("Rectangle"));

    const Interpreter::ObjectValue *ovProperty = lookupContext->context()->lookupType(doc.data(), QStringList() << "Item" << "states");
    QVERIFY(ovProperty);
    QCOMPARE(ovProperty->className(), QString("State"));

    const Interpreter::QmlObjectValue * qmlItemValue = dynamic_cast<const Interpreter::QmlObjectValue *>(ovItem);
    QVERIFY(qmlItemValue);
    QCOMPARE(qmlItemValue->defaultPropertyName(), QString("data"));
    QCOMPARE(qmlItemValue->propertyType("state"), QString("string"));

    const Interpreter::ObjectValue *ovState = lookupContext->context()->lookupType(doc.data(), QStringList() << "State");
    const Interpreter::QmlObjectValue * qmlState2Value = dynamic_cast<const Interpreter::QmlObjectValue *>(ovState);
    QCOMPARE(qmlState2Value->className(), QString("State"));

    const Interpreter::ObjectValue *ovImage = lookupContext->context()->lookupType(doc.data(), QStringList() << "Image");
    const Interpreter::QmlObjectValue * qmlImageValue = dynamic_cast<const Interpreter::QmlObjectValue *>(ovImage);
    QCOMPARE(qmlImageValue->className(), QString("Image"));
    QCOMPARE(qmlImageValue->propertyType("source"), QString("QUrl"));
}

using namespace QmlDesigner;

//void tst_Basic::testMetaInfo()
//{
//    TestModelManager modelManager;

//    modelManager.loadFile(QString(QTCREATORDIR) + "/tests/auto/qml/qmldesigner/data/fx/usingmybutton.qml");

//    Snapshot snapshot = modelManager.snapshot();
//    Document::Ptr doc = snapshot.document(QString(QTCREATORDIR) + "/tests/auto/qml/qmldesigner/data/fx/usingmybutton.qml");
//    QVERIFY(doc && doc->isQmlDocument());

//    LookupContext::Ptr lookupContext = LookupContext::create(doc, snapshot, QList<AST::Node*>());

//    MetaInfo::setDocument(doc);
//    MetaInfo::setLookupContext(lookupContext);

//    MetaInfo item("Qt/Item", 4, 7);
//    MetaInfo myButton("MyButton");
//    MetaInfo textEdit("TextEdit");
//    MetaInfo super("Qt/Super", 4, 7);
//    MetaInfo maniac("Maniac");

//    QVERIFY(item.isValid());
//    QVERIFY(myButton.isValid());
//    QVERIFY(textEdit.isValid());

//    QVERIFY(!super.isValid());
//    QVERIFY(!maniac.isValid());

//    QVERIFY(textEdit.localProperties().contains("color"));
//    QVERIFY(textEdit.isPropertyWritable("color"));
//    MetaInfo text("Qt/Text", 4, 7);

//    QVERIFY(item.isValid());
//    QVERIFY(myButton.isValid());

//    QVERIFY(!item.isComponent());
//    QVERIFY(myButton.isComponent());

//    QVERIFY(myButton.properties().contains("text"));
//    QVERIFY(myButton.properties().contains("myNumber"));
//    QVERIFY(myButton.properties().contains("gradient"));
//    QVERIFY(myButton.properties().contains("border.width"));
//    QVERIFY(myButton.properties().contains("anchors.bottomMargin"));
//    QVERIFY(myButton.localProperties().contains("text"));
//    QVERIFY(myButton.localProperties().contains("myNumber"));
//    QVERIFY(!myButton.localProperties().contains("border.width"));

//    QVERIFY(!item.properties().contains("text"));
//    QVERIFY(!item.properties().contains("myNumber"));
//    QVERIFY(item.properties().contains("anchors.bottomMargin"));
//    QVERIFY(item.properties().contains("x"));
//    QVERIFY(!item.localProperties().contains("x"));
//    QVERIFY(!item.localProperties().contains("enabled"));
//    QVERIFY(item.localProperties().contains("anchors.bottomMargin"));
//    QVERIFY(item.localProperties().contains("states"));
//    QVERIFY(item.localProperties().contains("parent"));

//    QCOMPARE(myButton.propertyTypeName("text"), QString("string"));
//    QCOMPARE(myButton.propertyTypeName("myNumber"), QString("real"));
//    QCOMPARE(myButton.propertyTypeName("x"), QString("qreal"));
//    QCOMPARE(myButton.propertyTypeName("border.width"), QString("int"));
//    QCOMPARE(myButton.propertyTypeName("gradient"), QString("Qt/Gradient"));

//    QCOMPARE(myButton.defaultPropertyName(), QString("content"));
//    QCOMPARE(item.defaultPropertyName(), QString("data"));

//    QVERIFY(item.isPropertyList("children"));
//    QVERIFY(myButton.isPropertyList("children"));
//    QVERIFY(item.isPropertyList("data"));
//    QVERIFY(myButton.isPropertyList("data"));
//    QVERIFY(item.isPropertyList("states"));
//    QVERIFY(myButton.isPropertyList("states"));

//    QVERIFY(!item.isPropertyList("x"));
//    QVERIFY(!myButton.isPropertyList("x"));
//    QVERIFY(!item.isPropertyList("state"));
//    QVERIFY(!myButton.isPropertyList("state"));

//    QVERIFY(myButton.isPropertyPointer("parent"));
//    QVERIFY(text.localProperties().contains("font"));
//    QVERIFY(!text.isPropertyPointer("font"));
//    QVERIFY(myButton.isPropertyWritable("state"));
//    QVERIFY(!myButton.isPropertyWritable("border"));

//    QVERIFY(text.localProperties().contains("horizontalAlignment"));
//    QVERIFY(text.isPropertyEnum("horizontalAlignment"));

//    foreach (Interpreter::ImportInfo import, doc->bind()->imports()) {
//        QCOMPARE(import.name(), QString("Qt"));
//    }
//}

QTEST_MAIN(tst_Basic);

#include "tst_basic.moc"
<commit_msg>QmlJS tests: Make basic test pass again.<commit_after>/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: Nokia Corporation (info@qt.nokia.com)
**
**
** GNU Lesser General Public License Usage
**
** This file may be used under the terms of the GNU Lesser General Public
** License version 2.1 as published by the Free Software Foundation and
** appearing in the file LICENSE.LGPL included in the packaging of this file.
** Please review the following information to ensure the GNU Lesser General
** Public License version 2.1 requirements will be met:
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights. These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** Other Usage
**
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Nokia.
**
** If you have questions regarding the use of this file, please contact
** Nokia at info@qt.nokia.com.
**
**************************************************************************/

#include <QScopedPointer>
#include <QLatin1String>
#include <QGraphicsObject>
#include <QApplication>
#include <QSettings>
#include <QFileInfo>

#include <qmljs/qmljsinterpreter.h>
#include <qmljs/qmljsdocument.h>
#include <qmljs/qmljsbind.h>
#include <qmljs/qmljscontext.h>
#include <qmljs/qmljslink.h>
#include <qmljs/parser/qmljsast_p.h>
#include <qmljstools/qmljsrefactoringchanges.h>
#include <qmljstools/qmljsmodelmanager.h>
#include <qmldesigner/designercore/include/metainfo.h>
#include <extensionsystem/pluginmanager.h>

#include <QtTest>

using namespace QmlJS;
using namespace QmlJSTools;

class tst_Basic : public QObject
{
    Q_OBJECT
public:
    tst_Basic();

private slots:
    void initTestCase();
    void cleanupTestCase();

    void basicObjectTests();
    //void testMetaInfo();

private:
    ExtensionSystem::PluginManager *pluginManager;
};

tst_Basic::tst_Basic()
{
}

#ifdef Q_OS_MAC
#  define SHARE_PATH "/Resources"
#else
#  define SHARE_PATH "/share/qtcreator"
#endif

QString resourcePath()
{
    return QDir::cleanPath(QTCREATORDIR + QLatin1String(SHARE_PATH));
}
void tst_Basic::initTestCase()
{
    pluginManager = new ExtensionSystem::PluginManager;
    QSettings *settings = new QSettings(QSettings::IniFormat, QSettings::UserScope,
                                 QLatin1String("Nokia"), QLatin1String("QtCreator"));
    pluginManager->setSettings(settings);
    pluginManager->setFileExtension(QLatin1String("pluginspec"));
    pluginManager->setPluginPaths(QStringList() << QLatin1String(Q_PLUGIN_PATH));
    pluginManager->loadPlugins();

    // the resource path is wrong, have to load things manually
    QFileInfo builtins(resourcePath() + "/qml-type-descriptions/builtins.qmltypes");
    QStringList errors, warnings;
    CppQmlTypesLoader::defaultQtObjects = CppQmlTypesLoader::loadQmlTypes(QFileInfoList() << builtins, &errors, &warnings);
}

void tst_Basic::cleanupTestCase()
{
    pluginManager->shutdown();
    delete pluginManager;
}

void tst_Basic::basicObjectTests()
{
    QmlJS::ModelManagerInterface *modelManager = QmlJS::ModelManagerInterface::instance();

    QStringList files(QString(QTCREATORDIR) + "/tests/auto/qml/qmldesigner/data/fx/usingmybutton.qml");
    modelManager->updateSourceFiles(files, false);
    modelManager->joinAllThreads();

    Snapshot snapshot = modelManager->snapshot();
    Document::Ptr doc = snapshot.document(QString(QTCREATORDIR) + "/tests/auto/qml/qmldesigner/data/fx/usingmybutton.qml");
    QVERIFY(doc && doc->isQmlDocument());

    ContextPtr context = Link(snapshot, QStringList(), LibraryInfo())();
    QVERIFY(context);

    QmlObjectValue *rectangleValue = context->valueOwner()->cppQmlTypes().typeByQualifiedName(
                "Qt", "Rectangle", LanguageUtils::ComponentVersion(4, 7));
    QVERIFY(rectangleValue);
    QVERIFY(!rectangleValue->isWritable("border"));
    QVERIFY(rectangleValue->hasProperty("border"));
    QVERIFY(rectangleValue->isPointer("border"));
    QVERIFY(rectangleValue->isWritable("color"));
    QVERIFY(!rectangleValue->isPointer("color"));

    const ObjectValue *ovItem = context->lookupType(doc.data(), QStringList() << "Item");
    QCOMPARE(ovItem->className(), QString("Item"));
    QCOMPARE(context->imports(doc.data())->info("Item", context.data()).name(), QString("Qt"));
    const ObjectValue *ovButton = context->lookupType(doc.data(), QStringList() << "MyButton");
    QCOMPARE(ovButton->className(), QString("MyButton"));
    QCOMPARE(ovButton->prototype(context)->className(), QString("Rectangle"));

    const ObjectValue *ovProperty = context->lookupType(doc.data(), QStringList() << "Item" << "states");
    QVERIFY(ovProperty);
    QCOMPARE(ovProperty->className(), QString("State"));

    const QmlObjectValue * qmlItemValue = dynamic_cast<const QmlObjectValue *>(ovItem);
    QVERIFY(qmlItemValue);
    QCOMPARE(qmlItemValue->defaultPropertyName(), QString("data"));
    QCOMPARE(qmlItemValue->propertyType("state"), QString("string"));

    const ObjectValue *ovState = context->lookupType(doc.data(), QStringList() << "State");
    const QmlObjectValue * qmlState2Value = dynamic_cast<const QmlObjectValue *>(ovState);
    QCOMPARE(qmlState2Value->className(), QString("State"));

    const ObjectValue *ovImage = context->lookupType(doc.data(), QStringList() << "Image");
    const QmlObjectValue * qmlImageValue = dynamic_cast<const QmlObjectValue *>(ovImage);
    QCOMPARE(qmlImageValue->className(), QString("Image"));
    QCOMPARE(qmlImageValue->propertyType("source"), QString("QUrl"));
}

using namespace QmlDesigner;

//void tst_Basic::testMetaInfo()
//{
//    TestModelManager modelManager;

//    modelManager.loadFile(QString(QTCREATORDIR) + "/tests/auto/qml/qmldesigner/data/fx/usingmybutton.qml");

//    Snapshot snapshot = modelManager.snapshot();
//    Document::Ptr doc = snapshot.document(QString(QTCREATORDIR) + "/tests/auto/qml/qmldesigner/data/fx/usingmybutton.qml");
//    QVERIFY(doc && doc->isQmlDocument());

//    LookupContext::Ptr lookupContext = LookupContext::create(doc, snapshot, QList<AST::Node*>());

//    MetaInfo::setDocument(doc);
//    MetaInfo::setLookupContext(lookupContext);

//    MetaInfo item("Qt/Item", 4, 7);
//    MetaInfo myButton("MyButton");
//    MetaInfo textEdit("TextEdit");
//    MetaInfo super("Qt/Super", 4, 7);
//    MetaInfo maniac("Maniac");

//    QVERIFY(item.isValid());
//    QVERIFY(myButton.isValid());
//    QVERIFY(textEdit.isValid());

//    QVERIFY(!super.isValid());
//    QVERIFY(!maniac.isValid());

//    QVERIFY(textEdit.localProperties().contains("color"));
//    QVERIFY(textEdit.isPropertyWritable("color"));
//    MetaInfo text("Qt/Text", 4, 7);

//    QVERIFY(item.isValid());
//    QVERIFY(myButton.isValid());

//    QVERIFY(!item.isComponent());
//    QVERIFY(myButton.isComponent());

//    QVERIFY(myButton.properties().contains("text"));
//    QVERIFY(myButton.properties().contains("myNumber"));
//    QVERIFY(myButton.properties().contains("gradient"));
//    QVERIFY(myButton.properties().contains("border.width"));
//    QVERIFY(myButton.properties().contains("anchors.bottomMargin"));
//    QVERIFY(myButton.localProperties().contains("text"));
//    QVERIFY(myButton.localProperties().contains("myNumber"));
//    QVERIFY(!myButton.localProperties().contains("border.width"));

//    QVERIFY(!item.properties().contains("text"));
//    QVERIFY(!item.properties().contains("myNumber"));
//    QVERIFY(item.properties().contains("anchors.bottomMargin"));
//    QVERIFY(item.properties().contains("x"));
//    QVERIFY(!item.localProperties().contains("x"));
//    QVERIFY(!item.localProperties().contains("enabled"));
//    QVERIFY(item.localProperties().contains("anchors.bottomMargin"));
//    QVERIFY(item.localProperties().contains("states"));
//    QVERIFY(item.localProperties().contains("parent"));

//    QCOMPARE(myButton.propertyTypeName("text"), QString("string"));
//    QCOMPARE(myButton.propertyTypeName("myNumber"), QString("real"));
//    QCOMPARE(myButton.propertyTypeName("x"), QString("qreal"));
//    QCOMPARE(myButton.propertyTypeName("border.width"), QString("int"));
//    QCOMPARE(myButton.propertyTypeName("gradient"), QString("Qt/Gradient"));

//    QCOMPARE(myButton.defaultPropertyName(), QString("content"));
//    QCOMPARE(item.defaultPropertyName(), QString("data"));

//    QVERIFY(item.isPropertyList("children"));
//    QVERIFY(myButton.isPropertyList("children"));
//    QVERIFY(item.isPropertyList("data"));
//    QVERIFY(myButton.isPropertyList("data"));
//    QVERIFY(item.isPropertyList("states"));
//    QVERIFY(myButton.isPropertyList("states"));

//    QVERIFY(!item.isPropertyList("x"));
//    QVERIFY(!myButton.isPropertyList("x"));
//    QVERIFY(!item.isPropertyList("state"));
//    QVERIFY(!myButton.isPropertyList("state"));

//    QVERIFY(myButton.isPropertyPointer("parent"));
//    QVERIFY(text.localProperties().contains("font"));
//    QVERIFY(!text.isPropertyPointer("font"));
//    QVERIFY(myButton.isPropertyWritable("state"));
//    QVERIFY(!myButton.isPropertyWritable("border"));

//    QVERIFY(text.localProperties().contains("horizontalAlignment"));
//    QVERIFY(text.isPropertyEnum("horizontalAlignment"));

//    foreach (ImportInfo import, doc->bind()->imports()) {
//        QCOMPARE(import.name(), QString("Qt"));
//    }
//}

QTEST_MAIN(tst_Basic);

#include "tst_basic.moc"
<|endoftext|>