Ejemplo n.º 1
0
void GPCreateFlashFrame( Bitmap& bmpSrc, CRect& rcBegin, CRect& rcEnd, Bitmap& bmpDes, CRect& rcDes, double dPercent )
{
	int  nWidth = bmpSrc.GetWidth();
	int  nHeight = bmpSrc.GetHeight();
	double dPercent2 = 1.0 - dPercent;
	int  nWidthDes = bmpDes.GetWidth();
	int  nHeightDes = bmpDes.GetHeight();
	CRect rcBmpSrc(0,0, nWidth, nHeight);
	CRect rcBmpDes(0,0, nWidthDes, nHeightDes);

	ASSERT(IsInRect(rcBegin, rcBmpSrc));
	ASSERT(IsInRect(rcEnd, rcBmpSrc));
	ASSERT(IsInRect(rcDes, rcBmpDes));
	ASSERT(rcBegin.Size() == rcEnd.Size());
	ASSERT(rcEnd.Size() == rcDes.Size());
	int nLoopWidth = rcBegin.Width();
	int nLoopHeight = rcBegin.Height();
	for (int i = 0; i <nLoopWidth; i++)
	{
		for (int j = 0; j < nLoopHeight; j++)
		{
			Color clrBegin;
			bmpSrc.GetPixel(i+ rcBegin.left, j+ rcBegin.top, &clrBegin);
			Color clrEnd;
			bmpSrc.GetPixel(i+ rcEnd.left, j+ rcEnd.top, &clrEnd);
			Color clrDes = Color::MakeARGB(clrBegin.GetA()*dPercent + clrEnd.GetA()* dPercent2, 
				clrBegin.GetR()*dPercent + clrEnd.GetR()* dPercent2,
				clrBegin.GetG()*dPercent + clrEnd.GetG()* dPercent2,
				clrBegin.GetB()*dPercent + clrEnd.GetB()* dPercent2);
			bmpDes.SetPixel(rcDes.left + i, rcDes.top+j, clrDes);
		}
	}
	

}
Ejemplo n.º 2
0
Color LimitedScaleColor (const Color& c1, const Color& c2, float factor)
{
	return Color ( LimitedScaleColor (c1.GetA(), c2.GetA(), factor)
				, LimitedScaleColor (c1.GetR(), c2.GetR(), factor)
				, LimitedScaleColor (c1.GetG(), c2.GetG(), factor)
				, LimitedScaleColor (c1.GetB(), c2.GetB(), factor));
}
Ejemplo n.º 3
0
  void MapPainterOpenGL::DrawPath(const Projection& /*projection*/,
                                  const MapParameter& /*parameter*/,
                                  const Color& color,
                                  double width,
                                  const std::vector<double>& /*dash*/,
                                  LineStyle::CapStyle /*startCap*/,
                                  LineStyle::CapStyle /*endCap*/,
                                  size_t transStart, size_t transEnd)
  {
    // TODO:
    // There is a limit in the OpenGL lineWidth, we need to
    // fallback to using quads instead of lines for this.

    glColor4d(color.GetR(),
              color.GetG(),
              color.GetB(),
              color.GetA());

    glLineWidth(width);

    glBegin(GL_LINE_STRIP);

    for (size_t i=transStart; i<=transEnd; i++) {
      glVertex3d(coordBuffer->buffer[i].GetX(),
                 coordBuffer->buffer[i].GetY(),
                 0.0);
    }

    glEnd();
  }
Ejemplo n.º 4
0
void MapPainterSVG::DrawPath(const Projection& projection,
                             const MapParameter& parameter,
                             const Color& color,
                             double width,
                             const std::vector<double>& dash,
                             LineStyle::CapStyle startCap,
                             LineStyle::CapStyle endCap,
                             size_t transStart, size_t transEnd)
{
    stream << "    <polyline";
    stream << " fill=\"none\"";
    stream << " stroke=\"" << GetColorValue(color) << "\"";

    if (!color.IsSolid()) {
        stream << " stroke-opacity=\"" << color.GetA() << "\"";
    }

    stream << " stroke-width=\"" << width << "\"";
    stream << std::endl;

    stream << "              points=\"";

    for (size_t i=transStart; i<=transEnd; i++) {
        if (i!=transStart) {
            stream << " ";
        }

        stream << coordBuffer->buffer[i].GetX() << "," << coordBuffer->buffer[i].GetY();

    }

    stream << "\" />" << std::endl;
}
Ejemplo n.º 5
0
void Image::draw(int x, int y, int x1, int y1, int w, int h, Scalar ScaleX, Scalar ScaleY, Scalar Rot, Color c)
{
    glPushMatrix();

        glTranslated(x+(Scalar)w/2,y+(Scalar)h/2,0);
        //DrawStuff
        glRotated(Rot,0,0,-1);

        glBindTexture(GL_TEXTURE_2D, imageTag);
        glBegin(GL_QUADS);
            glColor4d(c.GetR(),c.GetG(),c.GetB(),c.GetA());
            //tex((Scalar)x1/width,(Scalar)y1/height);
            glTexCoord2d((Scalar)(x1+0.375f)/texSize,Scalar(y1+0.375f)/texSize);
            glVertex2d(-.5*w*ScaleX,-.5*h*ScaleY);
            //tex((Scalar)x1/width,(Scalar)(y1+h)/height);
            glTexCoord2d((Scalar)(x1+0.357f)/texSize,(Scalar)(y1+h-0.625f)/texSize);
            glVertex2d(-.5*w*ScaleX,.5*h*ScaleY);
            //tex((Scalar)(x1+w)/width,(Scalar)(y1+h)/height);
            glTexCoord2d(((Scalar)x1+w-0.625f)/texSize,(Scalar)(y1+h-0.625f)/texSize);
            glVertex2d(.5*w*ScaleX,.5*h*ScaleY);
            //tex((Scalar)(x1+w)/width,(Scalar)y1/height);
            glTexCoord2d(((Scalar)x1+w-0.625f)/texSize,(Scalar)(y1+0.375f)/texSize);
            glVertex2d(.5*w*ScaleX,-.5*h*ScaleY);
        glEnd();

    glPopMatrix();
}
 int MapPainterCanvas::GetColorInt(Color color)
 {
   return GetColorInt(color.GetR(),
                      color.GetG(),
                      color.GetB(),
                      color.GetA());
 }
Ejemplo n.º 7
0
void CAggMemoryDC::Line(
    const PointF& p1,
    const PointF& p2, 
    const Color& clr)
{
    if (m_buf==0)
        return;

    CPoint _p1 = p1;
    CPoint _p2 = p2;

    pixel_format pixf(m_rbuf);
    ren_base renb(pixf);

    outline_renderer outline_rd(renb, s_lineprof);
    outline_rasterizer_aa outline_rt(outline_rd);

    outline_rd.profile(s_lineprof);
    outline_rd.color(agg::rgba8(clr.GetR(), clr.GetG(), clr.GetB(), clr.GetA()));
    outline_rd.clip_box(0, 0, m_rcUpdate.Width(), m_rcUpdate.Height());

    double p1x(p1.x), p1y(p1.y), p2x(p2.x), p2y(p2.y);
    m_mtx.transform(&p1x,&p1y);
    m_mtx.transform(&p2x,&p2y);

    outline_rt.move_to_d(p1x, p1y);
    outline_rt.line_to_d(p2x, p2y);
    //outline_rt.round_cap( 1 );
    outline_rt.render(false);
}
Ejemplo n.º 8
0
void CAggMemoryDC::Lines(
    const PointFVector& pts,
    const Color& clr,
    const REAL width/*=1.0*/)
{
    if (m_buf==0)
        return;

    unsigned count=pts.size()/2;
    ATLASSERT(count>0);
    ATLASSERT((float)count/2.0f>0);

    pixel_format pixf(m_rbuf);
    ren_base renb(pixf);
    solid_renderer ren_solid(renb);

    m_path.remove_all();

    for(unsigned i=0; i<count; ++i)
    {
        m_path.move_to(pts[i*2].x, pts[i*2].y);
        m_path.line_to(pts[i*2+1].x, pts[i*2+1].y);
    }

    typedef agg::conv_stroke<conv_path_trans_type> conv_stroke_outline;
    conv_stroke_outline stroke(m_transpath);
    stroke.width(width);

    m_ras_aa.add_path(stroke);

    ren_solid.color(agg::rgba8(clr.GetR(), clr.GetG(), clr.GetB(), clr.GetA()));
    agg::render_scanlines(m_ras_aa, m_sl, ren_solid);
}
Ejemplo n.º 9
0
void CAggMemoryDC::SolidPolygon(
    const PointF* points, 
    const Color& clr,
    unsigned point_count)
{
    if (m_buf==0)
        return;

    if(m_npaths>0)
    {
        m_pathCache.close_polygon();
        m_colorsCache[m_countpaths] = agg::rgba8(clr.GetR(), clr.GetG(), clr.GetB(), clr.GetA());
        m_path_idxCache[m_countpaths] = m_pathCache.start_new_path();
        m_countpaths++;
        m_pathCache.move_to(points->x, points->y);
        while(--point_count>0)
        {
            points++;
            m_pathCache.line_to(points->x, points->y);
        }
    }
    else
    {
        pixel_format pixf(m_rbuf);
        ren_base renb(pixf);
        solid_renderer ren_solid(renb);


        ATLASSERT(point_count>0);

        m_path.remove_all();
        m_path.move_to(points->x, points->y);
        while(--point_count>0)
        {
            points++;
            m_path.line_to(points->x, points->y);
        }

        m_ras_aa.reset();
        m_ras_aa.clip_box(0, 0, m_rcUpdate.Width(), m_rcUpdate.Height()); 
        m_ras_aa.add_path(m_transpath);

        ren_solid.color(agg::rgba8(clr.GetR(), clr.GetG(), clr.GetB(), clr.GetA()));
        agg::render_scanlines(m_ras_aa, m_sl, ren_solid);
    }
}
Ejemplo n.º 10
0
void CAggMemoryDC::SolidPolygon(
    const PointF* points, 
    const Color& clr,
    unsigned point_count,
    REAL widthoutline,
    const Color& clroutline )
{
    if (m_buf==0)
        return;

    ATLASSERT(point_count>0);

    pixel_format pixf(m_rbuf);
    ren_base renb(pixf);

    solid_renderer ren_solid(renb);
    m_ras_aa.reset();

    m_path.remove_all();
    m_path.move_to(points->x, points->y);
    while(--point_count>0)
    {
        points++;
        m_path.line_to(points->x, points->y);
    }
    m_path.close_polygon();

    m_ras_aa.add_path(m_transpath);
    ren_solid.color(agg::rgba8(clr.GetR(), clr.GetG(), clr.GetB(), clr.GetA()));
    agg::render_scanlines(m_ras_aa, m_sl, ren_solid);

    if(widthoutline>REAL(0))
    {
        typedef agg::conv_stroke<conv_path_trans_type> conv_trans_stroke_outline;
        ren_solid.color(agg::rgba8(clroutline.GetR(),
            clroutline.GetG(), clroutline.GetB(), clroutline.GetA()));
        conv_trans_stroke_outline stroke(m_transpath);
        stroke.width(widthoutline);
        m_ras_aa.add_path(stroke);
        agg::render_scanlines(m_ras_aa, m_sl, ren_solid);
    }
}
Ejemplo n.º 11
0
void Graphics::drawPoint(int x, int y, Color c)
{
    glPushMatrix();
    glLoadIdentity();
    //glScalef(ScaleX,ScaleY,1.0f);
    glTranslatef(0.375f,0.375f,0);
    //DrawStuff
    glDisable(GL_TEXTURE_2D);
    glBegin(GL_POINTS);
        glColor4d(c.GetR(),c.GetG(),c.GetB(),c.GetA());
        glVertex2d(x,y);
    glEnd();
    glEnable(GL_TEXTURE_2D);
    glPopMatrix();
}
Ejemplo n.º 12
0
void Graphics::drawLine(Scalar x, Scalar y, Scalar x2, Scalar y2, Color c)
{
    glPushMatrix();

    Scalar width = x2-x;

    Scalar height = (y2-y);

    glTranslated(x+width/2, y+height/2,0);

    //DrawStuff
    glDisable(GL_TEXTURE_2D);
    glBegin(GL_LINES);
        glColor4d(c.GetR(),c.GetG(),c.GetB(),c.GetA());
        glVertex2d(0-width/2,0-height/2);
        glVertex2d(width/2,height/2);
    glEnd();
    glEnable(GL_TEXTURE_2D);

    glPopMatrix();
}
Ejemplo n.º 13
0
/*
** Checks if the given point is inside the button.
**
*/
bool MeterButton::HitTest2(int px, int py)
{
	int x = GetX();
	int y = GetY();

	if (m_MouseOver &&
		px >= x && px < x + m_W &&
		py >= y && py < y + m_H)
	{
		if (m_SolidColor.GetA() != 0 || m_SolidColor2.GetA() != 0)
		{
			return true;
		}

		// Check transparent pixels
		if (m_Image.IsLoaded())
		{
			Rect meterRect = GetMeterRectPadding();
			int ix = meterRect.Width * m_State;
			px = px - meterRect.X + ix;
			py = py - meterRect.Y;
			if (px >= ix && px < ix + meterRect.Width &&
				py >= 0 && py < meterRect.Height)
			{
				Color color;
				Status status = m_Image.GetImage()->GetPixel(px, py, &color);
				if (status != Ok || color.GetA() != 0)
				{
					return true;
				}
			}
		}
		else
		{
			return true;
		}
	}
	return false;
}
Ejemplo n.º 14
0
void Graphics::fillRect(Rect r, Color c)
{
    glPushMatrix();

    glTranslated(r.getX()+(Scalar)r.getWidth()/2,r.getY()+(Scalar)r.getHeight()/2,0);
    //DrawStuff
    glRotated(r.getRot(),0,0,-1);

    int w = (int)r.getWidth();
    int h = (int)r.getHeight();

    glDisable(GL_TEXTURE_2D);
    glBegin(GL_QUADS);
        glColor4d(c.GetR(),c.GetG(),c.GetB(),c.GetA());
        glVertex2d(-.5*w,-.5*h);
        glVertex2d(-.5*w,.5*h);
        glVertex2d(.5*w,.5*h);
        glVertex2d(.5*w,-.5*h);
    glEnd();
    glEnable(GL_TEXTURE_2D);

    glPopMatrix();
}
Ejemplo n.º 15
0
bool Color::operator==(const Color & c) const
{
	return (r == c.GetR() && g == c.GetG() && b == c.GetB() && a == c.GetA());
}
Ejemplo n.º 16
0
void CAggMemoryDC::LinearGradientRect(
    const RectF& rc,
    const Color& clrstart,
    const Color& clrend,
    bool bHorizontal/*=true*/)
{
  // The gradient color array
    typedef agg::pod_auto_array<agg::rgba8, 256> color_array_type;

    // Gradient shape function (linear, radial, custom, etc)
    //-----------------
    typedef agg::gradient_xy gradient_func_type;   


    // Span interpolator. This object is used in all span generators 
    // that operate with transformations during iterating of the spans,
    // for example, image transformers use the interpolator too.
    //-----------------
    typedef agg::span_interpolator_linear<> interpolator_type;


    // Span allocator is an object that allocates memory for 
    // the array of colors that will be used to render the 
    // color spans. One object can be shared between different 
    // span generators.
    //-----------------
    typedef agg::span_allocator<agg::rgba8> span_allocator_type;


    // Finally, the gradient span generator working with the agg::rgba8 
    // color type. 
    // The 4-th argument is the color function that should have 
    // the [] operator returning the color in range of [0...255].
    // In our case it will be a simple look-up table of 256 colors.
    //-----------------
    typedef agg::span_gradient<agg::rgba8, 
                               interpolator_type, 
                               gradient_func_type, 
                               color_array_type> span_gradient_type;


    // The gradient scanline renderer type
    //-----------------
    typedef agg::renderer_scanline_aa<ren_base, 
                                    span_allocator_type,	
                                    span_gradient_type> renderer_gradient_type;


    // Common declarations (pixel format and basic renderer).
    //----------------
    pixel_format pixf(m_rbuf);
    ren_base renb(pixf);

    // The gradient objects declarations
    //----------------
    gradient_func_type  gradient_func;                   // The gradient function
    agg::trans_affine   gradient_mtx;                    // Affine transformer
    interpolator_type   span_interpolator(gradient_mtx); // Span interpolator
    span_allocator_type span_allocator;                  // Span Allocator
    color_array_type    color_array;                     // Gradient colors


    // Declare the gradient span itself. 
    // The last two arguments are so called "d1" and "d2" 
    // defining two distances in pixels, where the gradient starts
    // and where it ends. The actual meaning of "d1" and "d2" depands
    // on the gradient function.
    //----------------
    span_gradient_type span_gradient(span_interpolator, 
                                     gradient_func, 
                                     color_array, 
                                     bHorizontal ? rc.x : rc.y,
                                     bHorizontal ? rc.x+rc.Width : rc.y+rc.Height);

    // The gradient renderer
    //----------------
    renderer_gradient_type ren_gradient(renb, span_allocator, span_gradient);

    fill_color_array(color_array, 
                     agg::rgba8(clrstart.GetR(), clrstart.GetG(), clrstart.GetB(), clrstart.GetA()),
                     agg::rgba8(clrend.GetR(), clrend.GetG(), clrend.GetB(), clrend.GetA()));

    m_path.remove_all();
    m_path.move_to(rc.x, rc.y);
    m_path.line_to(rc.x+rc.Width, rc.y);
    m_path.line_to(rc.x+rc.Width, rc.y+rc.Height);
    m_path.line_to(rc.x, rc.y+rc.Height);
    m_path.close_polygon();

    m_ras_aa.reset();
    m_ras_aa.clip_box(0, 0, m_rcUpdate.Width(), m_rcUpdate.Height()); 
    m_ras_aa.add_path(m_transpath);
    agg::render_scanlines(m_ras_aa, m_sl, ren_gradient);
}
Ejemplo n.º 17
0
void CAggMemoryDC::Bezier(
    const GraphTypes::PointFVector& points,
    const Color& clr,
    double width/*=2.0*/,
    bool bArrow/*=true*/,
    bool bDashed/*=false*/,
    REAL scale/*=1.0*/,
    const GraphTypes::Color& firstClrControlPoint, 
    const GraphTypes::Color& lastClrControlPoint)
{
    if (m_buf==0)
        return;

    pixel_format pixf(m_rbuf);
    ren_base renb(pixf);
    solid_renderer ren_solid(renb);

    GraphTypes::PointFVector::const_iterator itr=points.begin();
    agg::curve_casteljau curve(points.size());
    curve.approximation_scale(scale);
    for(; itr!=points.end();++itr)
    {
        curve.add_vertex((*itr).x, (*itr).y);
    }

    m_ras_aa.clip_box(0, 0, m_rcUpdate.Width(), m_rcUpdate.Height()); 

    typedef agg::conv_dash<agg::curve_casteljau, agg::vcgen_markers_term> conv_dash_type;
    typedef agg::conv_stroke<agg::curve_casteljau, agg::vcgen_markers_term> conv_stroke_type;
    typedef agg::conv_stroke<conv_dash_type, agg::vcgen_markers_term> conv_dash_stroke_type;

    if(bDashed)
    {
        conv_dash_type dash(curve);
        conv_dash_stroke_type stroke(dash);
        stroke.width(width);

        double k = ::pow(stroke.width(), 0.7)*scale;

        if(bArrow)
        {
            // can't use AddPathWithArrowHead(m_ras_aa, stroke, m_mtx, 10, 3, 2, scale);
            agg::arrowhead ah;
            ah.head(0, (10-2) * k, 3 * k, 2 * k);
            typedef agg::conv_marker<agg::vcgen_markers_term, agg::arrowhead> arrow_type;
            // arrow must take markers before dash markers are added
            arrow_type arrow(dash.markers(), ah);

            dash.add_dash(k*4, k*4);
            dash.dash_start(k*4);

            stroke.shorten(k*(10-2));
            typedef agg::conv_transform<conv_dash_stroke_type, agg::trans_affine> conv_trans_type;
            conv_trans_type pathtrans(stroke, m_mtx);
            m_ras_aa.add_path(pathtrans);

            typedef agg::conv_transform<arrow_type, agg::trans_affine> arrow_trans_type;
            arrow_trans_type arrowtrans(arrow, m_mtx); 
            m_ras_aa.add_path(arrowtrans);
        }
        else
        {
            typedef agg::conv_transform<conv_dash_stroke_type, agg::trans_affine> conv_trans_type;
            dash.add_dash(k*4, k*4);
            dash.dash_start(k*4);
            conv_trans_type transstroke(stroke, m_mtx);
            m_ras_aa.add_path(transstroke);
        }
    }
    else
    {
        conv_stroke_type stroke(curve);
        stroke.width(width);

        if(bArrow)
            AddPathWithArrowHead(m_ras_aa, stroke, m_mtx, 10, 3, 2, scale);
        else
        {
            typedef agg::conv_transform<conv_stroke_type, agg::trans_affine> conv_trans_type;
            conv_trans_type transstroke(stroke, m_mtx);
            m_ras_aa.add_path(transstroke);
        }
    }

    ren_solid.color(agg::rgba8(clr.GetR(), clr.GetG(), clr.GetB(), clr.GetA()));
    agg::render_scanlines(m_ras_aa, m_sl, ren_solid);

    if (points.size() > 1)
    {
        if (firstClrControlPoint.GetValue())
        {
            agg::ellipse el_start(points[0].x, points[0].y, 4 * scale, 4 * scale, 16);
            m_ras_aa.add_path(el_start);
            ren_solid.color(agg::rgba8(firstClrControlPoint.GetR(), firstClrControlPoint.GetG(), firstClrControlPoint.GetB(), firstClrControlPoint.GetA()));
            agg::render_scanlines(m_ras_aa, m_sl, ren_solid);
        }
        if (lastClrControlPoint.GetValue())
        {
            agg::ellipse el_end(points[points.size() - 1].x, points[points.size() - 1].y, 4 * scale, 4 * scale, 16);
            m_ras_aa.add_path(el_end);
            ren_solid.color(agg::rgba8(lastClrControlPoint.GetR(), lastClrControlPoint.GetG(), lastClrControlPoint.GetB(), lastClrControlPoint.GetA()));
            agg::render_scanlines(m_ras_aa, m_sl, ren_solid);
        }
    }
}
Ejemplo n.º 18
0
bool Color::operator!=(const Color & c) const
{
	return (r != c.GetR() || g != c.GetG() || b != c.GetB() || a != c.GetA());
}
Ejemplo n.º 19
0
  void MapPainterAgg::DrawPath(const Projection& projection,
                               const MapParameter& parameter,
                               const Color& color,
                               double width,
                               const std::vector<double>& dash,
                               LineStyle::CapStyle startCap,
                               LineStyle::CapStyle endCap,
                               size_t transStart, size_t transEnd)
  {
    agg::path_storage p;

    for (size_t i=transStart; i<=transEnd; i++) {
      if (i==transStart) {
        p.move_to(coordBuffer->buffer[i].GetX(),
                  coordBuffer->buffer[i].GetY());
      }
      else {
        p.line_to(coordBuffer->buffer[i].GetX(),
                  coordBuffer->buffer[i].GetY());
      }
    }

    renderer_aa->color(agg::rgba(color.GetR(),
                                 color.GetG(),
                                 color.GetB(),
                                 color.GetA()));

    if (dash.empty()) {
      agg::conv_stroke<agg::path_storage> stroke(p);

      stroke.width(width);

      if (startCap==LineStyle::capButt ||
          endCap==LineStyle::capButt) {
        stroke.line_cap(agg::butt_cap);
      }
      else if (startCap==LineStyle::capSquare ||
               endCap==LineStyle::capSquare) {
        stroke.line_cap(agg::square_cap);
      }
      else {
        stroke.line_cap(agg::round_cap);
      }

      rasterizer->add_path(stroke);

      agg::render_scanlines(*rasterizer,*scanlineP8,*renderer_aa);
    }
    else {
      agg::conv_dash<agg::path_storage>                    dasher(p);
      agg::conv_stroke<agg::conv_dash<agg::path_storage> > stroke(dasher);

      stroke.width(width);

      if (startCap==LineStyle::capButt ||
          endCap==LineStyle::capButt) {
        stroke.line_cap(agg::butt_cap);
      }
      else if (startCap==LineStyle::capSquare ||
               endCap==LineStyle::capSquare) {
        stroke.line_cap(agg::square_cap);
      }
      else {
        stroke.line_cap(agg::round_cap);
      }

      for (size_t i=0; i<dash.size(); i+=2) {
        dasher.add_dash(dash[i]*width,dash[i+1]*width);
      }

      rasterizer->add_path(stroke);

      agg::render_scanlines(*rasterizer,*scanlineP8,*renderer_aa);
    }

    // TODO: End point caps "dots"
  }
Ejemplo n.º 20
0
void SystemDraw::Text(int x, int y, int angle, const wchar *text, Font font, Color ink, int outlineStrength, Color outlineColor, int glowStrength, Color glowColor, int n, const int *dx)
{
	if(!text)
		return;

	const wchar* s = text;
	OpenGLFont& fi = resources.GetFont(font);
	
	glEnable(GL_TEXTURE_2D);
	
	#if CLIP_MODE == 3
	float cl = (float) clip.left;
	float ct = (float) clip.top;
	float cr = (float) clip.right;
	float cb = (float) clip.bottom;
	#endif
	fi.UpdateTextures();	

	glEnableClientState(GL_TEXTURE_COORD_ARRAY);
	alphaMagProg.Start();

	const float ic = 1.f / 255.f;

	float outlineCenter = ConvStrength(0.1f, 0.45f, (float) 100 - min(outlineStrength, 100));
	float glowCenter = ConvStrength(0.55f, 0.95f, (float) 100 - min(glowStrength, 100));
	
	#ifdef COLOR_ALPHA
	float ac = alpha / 255.f;
	int inkA = ink.GetA();
	int outlineA = outlineColor.GetA();
	int glowA = glowColor.GetA();
	#else
	float ac = alpha;
	int inkA = 1;
	int outlineA = 1;
	int glowA = 1;
	#endif
	
	alphaMagProg.SetUniform("GlyphColor", ink.GetR() * ic, ink.GetG() * ic, ink.GetB() * ic, inkA * ic * ac);
	alphaMagProg.SetUniform("OutlineColor", outlineColor.GetR() * ic, outlineColor.GetG() * ic, outlineColor.GetB() * ic, outlineA * ic * ac);
	alphaMagProg.SetUniform("GlowColor", glowColor.GetR() * ic, glowColor.GetG() * ic, glowColor.GetB() * ic, glowA * ic * ac);
	alphaMagProg.SetUniform("Outline", outlineStrength > 0); //0.1 - 0.45
	alphaMagProg.SetUniform("Glow", glowStrength > 0); //0.55 - 0.095
	alphaMagProg.SetUniform("Shadow", false);
	alphaMagProg.SetUniform("OutlineCenter", outlineCenter);
	alphaMagProg.SetUniform("GlowCenter", glowCenter);
	
	float xp = (float) x;
	float yp = (float) y;

	int page = -1;

	float sw = (float) fi.scaleW;
	float sh = (float) fi.scaleH;
	
	float tw = 1.f / sw;
	float th = 1.f / sh;

	while(*s && n > 0)
	{
		int cn = *s;
		
		if(cn >= 0 && cn < fi.chars.GetCount())
		{
			const OpenGLFont::CharInfo& ci = fi.chars[cn];
	
			cn <<= 3;
	
			if(ci.page != page)
			{
				resources.Bind(fi.pages[ci.page], Resources::LINEAR_FILTERING);
				glActiveTexture(GL_TEXTURE0);
				alphaMagProg.SetUniform("Texture", 0);
				page = ci.page;
			}
			
			float sx = (float) ci.xoffset * fi.scale + xp + drawing_offset.x;
			float sy = (float) ci.yoffset * fi.scale + yp + drawing_offset.y;
			float dx = sx + ci.width * fi.scale;
			float dy = sy + ci.height * fi.scale;

			#if CLIP_MODE == 3
			if(sx <= clip.right && sy <= clip.bottom && dx >= clip.left && dy >= clip.top)
			#endif
			{
				float tl = (float) ci.x;
				float tt = (float) ci.y;
				float tr = (float) ci.x + ci.width;
				float tb = (float) ci.y + ci.height;
								
				#if CLIP_MODE == 3
				if(sx < cl)
				{
					tl += (cl - sx);
					sx = cl;
				}
				
				if(sy < ct)
				{
					tt += (ct - sy);
					sy = ct;
				}
				
				if(dx > cr)
				{
					tr -= dx - cr;
					dx = cr;
				}
				
				if(dy > cb)
				{
					tb -= dy - cb;
					dy = cb;
				}
				#endif

				tl = (tl + TEXEL_OFFSET) * tw;
				tr = (tr - TEXEL_OFFSET) * tw;
				tt = (tt + TEXEL_OFFSET) * th;
				tb = (tb - TEXEL_OFFSET) * th;
				/*
				float vtx[] = {
					sx, dy,
					sx, sy,
					dx, dy,
					dx, sy
				};*/
				float vtx[12];
				SetVtx(vtx, sx, sy, dx, dy);

				float crd[] = {
					tl, tb,
					tl, tt,
					tr, tb,
					tr, tt
				};
			
				glTexCoordPointer(2, GL_FLOAT, 0, crd);
				glVertexPointer(projection_mode ? 3: 2, GL_FLOAT, 0, vtx);
				glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
			}
	
			xp += int(ci.xadvance * fi.scale + 0.5f);
			
			int k = fi.kerns.Find(*s);
			if(k >= 0)
				xp += int(fi.kerns[k].Get(*(s + 1), 0) * fi.scale);
		}
		
		++s;
		--n;
	}

	alphaMagProg.Stop();
	glDisableClientState(GL_TEXTURE_COORD_ARRAY);
	glDisable(GL_TEXTURE_2D);
}