ps_font* PICAPI ps_font_create(const char* name, ps_charset c, float s, int w, ps_bool i) { if (!picasso::is_valid_system_device()) { global_status = STATUS_DEVICE_ERROR; return 0; } if (!name) { global_status = STATUS_INVALID_ARGUMENT; return 0; } ps_font* f = (ps_font*)mem_malloc(sizeof(ps_font)); if (f) { f->refcount = 1; new ((void*)&(f->desc)) picasso::font_desc(name); f->desc.set_charset(c); f->desc.set_height(FLT_TO_SCALAR(s)); f->desc.set_weight(FLT_TO_SCALAR(w)); f->desc.set_italic(i ? true: false); f->desc.set_hint(true); f->desc.set_flip_y(true); global_status = STATUS_SUCCEED; return f; } else { global_status = STATUS_OUT_OF_MEMORY; return 0; } }
void PICAPI ps_show_glyphs(ps_context* ctx, float x, float y, ps_glyph* g, unsigned int len) { if (!picasso::is_valid_system_device()) { global_status = STATUS_DEVICE_ERROR; return; } if (!ctx || !g || !len) { global_status = STATUS_INVALID_ARGUMENT; return; } scalar gx = FLT_TO_SCALAR(x); scalar gy = FLT_TO_SCALAR(y); if (create_device_font(ctx)) { gy += ctx->fonts->current_font()->ascent(); for (unsigned int i = 0; i < len; i++) { const picasso::glyph* glyph = (const picasso::glyph*)g[i].glyph; if (glyph) { if (ctx->font_kerning) ctx->fonts->current_font()->add_kerning(&gx, &gy); if (ctx->fonts->current_font()->generate_raster(glyph, gx, gy)) ctx->canvas->p->render_glyph(ctx->state, ctx->raster, ctx->fonts->current_font(), glyph->type); gx += glyph->advance_x; gy += glyph->advance_y; } } ctx->canvas->p->render_glyphs_raster(ctx->state, ctx->raster, ctx->font_render_type); } global_status = STATUS_SUCCEED; }
ps_bool PICAPI ps_path_contains(const ps_path* path, const ps_point* p, ps_fill_rule rule) { if (!picasso::is_valid_system_device()) { global_status = STATUS_DEVICE_ERROR; return False; } if (!path || !p) { global_status = STATUS_INVALID_ARGUMENT; return False; } if (!path->path.total_vertices()) { global_status = STATUS_SUCCEED; return False; } ps_rect br = picasso::_path_bounding_rect(path->path); if ((p->x < br.x) || (p->y < br.y) || (p->x > (br.x+br.w)) || (p->y > (br.y+br.h))) { //out of bounding rect global_status = STATUS_SUCCEED; return False; } global_status = STATUS_SUCCEED; return picasso::raster_adapter::fill_contents_point(path->path, FLT_TO_SCALAR(p->x), FLT_TO_SCALAR(p->y), (picasso::filling_rule)rule) ? True : False; }
//curve4_inc void curve4_inc::init(scalar x1, scalar y1, scalar x2, scalar y2, scalar x3, scalar y3, scalar x4, scalar y4) { m_start_x = x1; m_start_y = y1; m_end_x = x4; m_end_y = y4; scalar dx1 = x2 - x1; scalar dy1 = y2 - y1; scalar dx2 = x3 - x2; scalar dy2 = y3 - y2; scalar dx3 = x4 - x3; scalar dy3 = y4 - y3; scalar len = (Sqrt(dx1 * dx1 + dy1 * dy1) + Sqrt(dx2 * dx2 + dy2 * dy2) + Sqrt(dx3 * dx3 + dy3 * dy3)) * FLT_TO_SCALAR(0.25f) * m_scale; m_num_steps = uround(len); if (m_num_steps < 4) { m_num_steps = 4; } scalar subdivide_step = FLT_TO_SCALAR(1.0f) / m_num_steps; scalar subdivide_step2 = subdivide_step * subdivide_step; scalar subdivide_step3 = subdivide_step * subdivide_step * subdivide_step; scalar pre1 = FLT_TO_SCALAR(3.0f) * subdivide_step; scalar pre2 = FLT_TO_SCALAR(3.0f) * subdivide_step2; scalar pre4 = FLT_TO_SCALAR(6.0f) * subdivide_step2; scalar pre5 = FLT_TO_SCALAR(6.0f) * subdivide_step3; scalar tmp1x = x1 - x2 * FLT_TO_SCALAR(2.0f) + x3; scalar tmp1y = y1 - y2 * FLT_TO_SCALAR(2.0f) + y3; scalar tmp2x = (x2 - x3) * FLT_TO_SCALAR(3.0f) - x1 + x4; scalar tmp2y = (y2 - y3) * FLT_TO_SCALAR(3.0f) - y1 + y4; m_saved_fx = m_fx = x1; m_saved_fy = m_fy = y1; m_saved_dfx = m_dfx = (x2 - x1) * pre1 + tmp1x * pre2 + tmp2x * subdivide_step3; m_saved_dfy = m_dfy = (y2 - y1) * pre1 + tmp1y * pre2 + tmp2y * subdivide_step3; m_saved_ddfx = m_ddfx = tmp1x * pre4 + tmp2x * pre5; m_saved_ddfy = m_ddfy = tmp1y * pre4 + tmp2y * pre5; m_dddfx = tmp2x * pre5; m_dddfy = tmp2y * pre5; m_step = m_num_steps; }
void PICAPI ps_path_move_to(ps_path* path, const ps_point* p) { if (!picasso::is_valid_system_device()) { global_status = STATUS_DEVICE_ERROR; return; } if (!path || !p) { global_status = STATUS_INVALID_ARGUMENT; return; } path->path.move_to(FLT_TO_SCALAR(p->x), FLT_TO_SCALAR(p->y)); global_status = STATUS_SUCCEED; }
void PICAPI ps_set_text_stroke_color(ps_context* ctx, const ps_color* c) { if (!picasso::is_valid_system_device()) { global_status = STATUS_DEVICE_ERROR; return; } if (!ctx || !c) { global_status = STATUS_INVALID_ARGUMENT; return; } ctx->state->font_scolor = picasso::rgba(FLT_TO_SCALAR(c->r), FLT_TO_SCALAR(c->g), FLT_TO_SCALAR(c->b), FLT_TO_SCALAR(c->a)); global_status = STATUS_SUCCEED; }
void PICAPI ps_mask_add_color_filter(ps_mask* mask, const ps_color* c) { if (!picasso::is_valid_system_device()) { global_status = STATUS_DEVICE_ERROR; return; } if (!mask || !c) { global_status = STATUS_INVALID_ARGUMENT; return; } mask->mask.set_mask_type(MASK_COLORS); mask->mask.add_filter_color(picasso::rgba(FLT_TO_SCALAR(c->r), FLT_TO_SCALAR(c->g), FLT_TO_SCALAR(c->b), FLT_TO_SCALAR(c->a))); global_status = STATUS_SUCCEED; }
//curve3_div void curve3_div::init(scalar x1, scalar y1, scalar x2, scalar y2, scalar x3, scalar y3) { m_points.clear(); m_distance_tolerance_square = FLT_TO_SCALAR(0.5f) / m_approximation_scale; m_distance_tolerance_square *= m_distance_tolerance_square; bezier(x1, y1, x2, y2, x3, y3); m_count = 0; }
void gfx_gradient_adapter::init_radial(int spread, scalar x1, scalar y1, scalar radius1, scalar x2, scalar y2, scalar radius2) { if (!m_wrapper) { if ((x1 == x2) && (y1 == y2)) { switch (spread) { case SPREAD_PAD: m_wrapper = new gfx_gradient<gradient_radial, gradient_pad_adaptor<gradient_radial> >; break; case SPREAD_REPEAT: m_wrapper = new gfx_gradient<gradient_radial, gradient_repeat_adaptor<gradient_radial> >; break; case SPREAD_REFLECT: m_wrapper = new gfx_gradient<gradient_radial, gradient_reflect_adaptor<gradient_radial> >; break; } } else { switch (spread) { case SPREAD_PAD: m_wrapper = new gfx_gradient<gradient_radial_focus, gradient_pad_adaptor<gradient_radial_focus> >; break; case SPREAD_REPEAT: m_wrapper = new gfx_gradient<gradient_radial_focus, gradient_repeat_adaptor<gradient_radial_focus> >; break; case SPREAD_REFLECT: m_wrapper = new gfx_gradient<gradient_radial_focus, gradient_reflect_adaptor<gradient_radial_focus> >; break; } } if (!m_wrapper) return; scalar len = Fabs(radius2); scalar fx = x1 - x2; scalar fy = y1 - y2; m_wrapper->init(len, fx, fy); if (!len) len = FLT_TO_SCALAR(2.0f); // len can not be zero gfx_trans_affine mtx; mtx.translate(x1 - fx, y1 - fy); m_start = Fabs(radius1); m_length = len; m_matrix = mtx; } }
void PICAPI ps_path_add_rect(ps_path* path, const ps_rect* r) { if (!picasso::is_valid_system_device()) { global_status = STATUS_DEVICE_ERROR; return; } if (!path || !r) { global_status = STATUS_INVALID_ARGUMENT; return; } path->path.move_to(FLT_TO_SCALAR(r->x), FLT_TO_SCALAR(r->y)); path->path.hline_rel(FLT_TO_SCALAR(r->w)); path->path.vline_rel(FLT_TO_SCALAR(r->h)); path->path.hline_rel(-FLT_TO_SCALAR(r->w)); path->path.end_poly(); global_status = STATUS_SUCCEED; }
void PICAPI ps_path_bezier_to(ps_path* path, const ps_point* cp1, const ps_point* cp2, const ps_point* ep) { if (!picasso::is_valid_system_device()) { global_status = STATUS_DEVICE_ERROR; return; } if (!path || !cp1 || !cp2 || !ep) { global_status = STATUS_INVALID_ARGUMENT; return; } picasso::curve4 c(path->path.last_x(), path->path.last_y(), FLT_TO_SCALAR(cp1->x), FLT_TO_SCALAR(cp1->y), FLT_TO_SCALAR(cp2->x), FLT_TO_SCALAR(cp2->y), FLT_TO_SCALAR(ep->x), FLT_TO_SCALAR(ep->y)); if (picasso::_is_closed_path(path->path)) path->path.concat_path(c, 0); else path->path.join_path(c, 0); global_status = STATUS_SUCCEED; }
void PICAPI ps_path_add_ellipse(ps_path* path, const ps_rect* r) { if (!picasso::is_valid_system_device()) { global_status = STATUS_DEVICE_ERROR; return; } if (!path || !r) { global_status = STATUS_INVALID_ARGUMENT; return; } picasso::ellipse e(FLT_TO_SCALAR(r->x+r->w/2), FLT_TO_SCALAR(r->y+r->h/2), FLT_TO_SCALAR(r->w/2), FLT_TO_SCALAR(r->h/2)); if (picasso::_is_closed_path(path->path)) path->path.concat_path(e, 0); else path->path.join_path(e, 0); global_status = STATUS_SUCCEED; }
void PICAPI ps_image_set_transparent_color(ps_image* img, const ps_color* c) { if (!picasso::is_valid_system_device()) { global_status = STATUS_DEVICE_ERROR; return; } if (!img) { global_status = STATUS_INVALID_ARGUMENT; return; } if (!c) { img->buffer.clear_color_channel(); } else { img->buffer.set_color_channel(picasso::rgba(FLT_TO_SCALAR(c->r), FLT_TO_SCALAR(c->g),FLT_TO_SCALAR(c->b),FLT_TO_SCALAR(c->a))); } global_status = STATUS_SUCCEED; }
void PICAPI ps_path_add_arc(ps_path* path, const ps_point* cp, float r, float sa, float ea, ps_bool cw) { if (!picasso::is_valid_system_device()) { global_status = STATUS_DEVICE_ERROR; return; } if (!path || !cp) { global_status = STATUS_INVALID_ARGUMENT; return; } if (r <= 0.0f) { global_status = STATUS_SUCCEED; return; } picasso::arc a(FLT_TO_SCALAR(cp->x), FLT_TO_SCALAR(cp->y), FLT_TO_SCALAR(r), FLT_TO_SCALAR(r), FLT_TO_SCALAR(sa), FLT_TO_SCALAR(ea), (cw ? true : false)); if (picasso::_is_closed_path(path->path)) path->path.concat_path(a, 0); else path->path.join_path(a, 0); global_status = STATUS_SUCCEED; }
void PICAPI ps_wide_text_out_length(ps_context* ctx, float x, float y, const ps_uchar16* text, unsigned int len) { if (!picasso::is_valid_system_device()) { global_status = STATUS_DEVICE_ERROR; return; } if (!ctx || !text || !len) { global_status = STATUS_INVALID_ARGUMENT; return; } scalar gx = FLT_TO_SCALAR(x); scalar gy = FLT_TO_SCALAR(y); if (create_device_font(ctx)) { gy += ctx->fonts->current_font()->ascent(); const ps_uchar16* p = text; while (*p && len) { register ps_uchar16 c = *p; const picasso::glyph* glyph = ctx->fonts->current_font()->get_glyph(c); if (glyph) { if (ctx->font_kerning) ctx->fonts->current_font()->add_kerning(&gx, &gy); if (ctx->fonts->current_font()->generate_raster(glyph, gx, gy)) ctx->canvas->p->render_glyph(ctx->state, ctx->raster, ctx->fonts->current_font(), glyph->type); gx += glyph->advance_x; gy += glyph->advance_y; } len--; p++; } ctx->canvas->p->render_glyphs_raster(ctx->state, ctx->raster, ctx->font_render_type); } global_status = STATUS_SUCCEED; }
// gfx gradient adapter void gfx_gradient_adapter::init_linear(int spread, scalar x1, scalar y1, scalar x2, scalar y2) { if (!m_wrapper) { switch (spread) { case SPREAD_PAD: m_wrapper = new gfx_gradient<gradient_x, gradient_pad_adaptor<gradient_x> >; break; case SPREAD_REPEAT: m_wrapper = new gfx_gradient<gradient_x, gradient_repeat_adaptor<gradient_x> >; break; case SPREAD_REFLECT: m_wrapper = new gfx_gradient<gradient_x, gradient_reflect_adaptor<gradient_x> >; break; }; if (!m_wrapper) return; scalar len = calc_distance(x1, y1, x2, y2); gfx_trans_affine mtx; if (len) { if (x2 < x1) mtx.rotate(PI - Asin((y2 - y1) / len)); else mtx.rotate(Asin((y2 - y1) / len)); } else len = FLT_TO_SCALAR(2.0f); // len can not be zero mtx.translate(x1, y1); m_start = FLT_TO_SCALAR(0.0f); m_length = len; m_matrix = mtx; } }
void PICAPI ps_font_set_size(ps_font* f, float s) { if (!picasso::is_valid_system_device()) { global_status = STATUS_DEVICE_ERROR; return; } if (!f || s < 0.0) { global_status = STATUS_INVALID_ARGUMENT; return; } f->desc.set_height(FLT_TO_SCALAR(s)); global_status = STATUS_SUCCEED; }
void PICAPI ps_font_set_weight(ps_font* f, int w) { if (!picasso::is_valid_system_device()) { global_status = STATUS_DEVICE_ERROR; return; } if (!f || w < 100 || w > 900) { global_status = STATUS_INVALID_ARGUMENT; return; } f->desc.set_weight(FLT_TO_SCALAR(w)); global_status = STATUS_SUCCEED; }
void PICAPI ps_path_arc_to(ps_path* path, float rx, float ry, float a, ps_bool large, ps_bool cw, const ps_point* ep) { if (!picasso::is_valid_system_device()) { global_status = STATUS_DEVICE_ERROR; return; } if (!path || !ep || rx <= 0.0 || ry <= 0.0) { global_status = STATUS_INVALID_ARGUMENT; return; } scalar x1 = path->path.last_x(); scalar y1 = path->path.last_y(); picasso::bezier_arc_svg arc(x1, y1, FLT_TO_SCALAR(rx), FLT_TO_SCALAR(ry), FLT_TO_SCALAR(a), (large ? true : false), (cw ? true : false), FLT_TO_SCALAR(ep->x), FLT_TO_SCALAR(ep->y)); picasso::conv_curve cr(arc); if (picasso::_is_closed_path(path->path)) path->path.concat_path(cr, 0); else path->path.join_path(cr, 0); global_status = STATUS_SUCCEED; }
// curve3_inc void curve3_inc::init(scalar x1, scalar y1, scalar x2, scalar y2, scalar x3, scalar y3) { m_start_x = x1; m_start_y = y1; m_end_x = x3; m_end_y = y3; scalar dx1 = x2 - x1; scalar dy1 = y2 - y1; scalar dx2 = x3 - x2; scalar dy2 = y3 - y2; scalar len = Sqrt(dx1 * dx1 + dy1 * dy1) + Sqrt(dx2 * dx2 + dy2 * dy2); m_num_steps = uround(len * FLT_TO_SCALAR(0.25f) * m_scale); if (m_num_steps < 4) { m_num_steps = 4; } scalar subdivide_step = FLT_TO_SCALAR(1.0f) / m_num_steps; scalar subdivide_step2 = subdivide_step * subdivide_step; scalar tmpx = (x1 - x2 * FLT_TO_SCALAR(2.0f) + x3) * subdivide_step2; scalar tmpy = (y1 - y2 * FLT_TO_SCALAR(2.0f) + y3) * subdivide_step2; m_saved_fx = m_fx = x1; m_saved_fy = m_fy = y1; m_saved_dfx = m_dfx = tmpx + (x2 - x1) * (FLT_TO_SCALAR(2.0f) * subdivide_step); m_saved_dfy = m_dfy = tmpy + (y2 - y1) * (FLT_TO_SCALAR(2.0f) * subdivide_step); m_ddfx = tmpx * FLT_TO_SCALAR(2.0f); m_ddfy = tmpy * FLT_TO_SCALAR(2.0f); m_step = m_num_steps; }
scalar calc_weight(scalar x) const { return FLT_TO_SCALAR(1.0f) - x; }
scalar calc_weight(scalar x) const { return Exp(-FLT_TO_SCALAR(2.0f) * x * x) * Sqrt(FLT_TO_SCALAR(2.0f) * _1divPI); }
namespace picasso { const scalar curve_distance_epsilon = FLT_TO_SCALAR(1e-30f); const scalar curve_collinearity_epsilon = FLT_TO_SCALAR(1e-30f); const scalar curve_angle_tolerance_epsilon = FLT_TO_SCALAR(0.01f); enum curve_recursion_limit { curve_recursion_limit = 32, }; // curve3_inc void curve3_inc::init(scalar x1, scalar y1, scalar x2, scalar y2, scalar x3, scalar y3) { m_start_x = x1; m_start_y = y1; m_end_x = x3; m_end_y = y3; scalar dx1 = x2 - x1; scalar dy1 = y2 - y1; scalar dx2 = x3 - x2; scalar dy2 = y3 - y2; scalar len = Sqrt(dx1 * dx1 + dy1 * dy1) + Sqrt(dx2 * dx2 + dy2 * dy2); m_num_steps = uround(len * FLT_TO_SCALAR(0.25f) * m_scale); if (m_num_steps < 4) { m_num_steps = 4; } scalar subdivide_step = FLT_TO_SCALAR(1.0f) / m_num_steps; scalar subdivide_step2 = subdivide_step * subdivide_step; scalar tmpx = (x1 - x2 * FLT_TO_SCALAR(2.0f) + x3) * subdivide_step2; scalar tmpy = (y1 - y2 * FLT_TO_SCALAR(2.0f) + y3) * subdivide_step2; m_saved_fx = m_fx = x1; m_saved_fy = m_fy = y1; m_saved_dfx = m_dfx = tmpx + (x2 - x1) * (FLT_TO_SCALAR(2.0f) * subdivide_step); m_saved_dfy = m_dfy = tmpy + (y2 - y1) * (FLT_TO_SCALAR(2.0f) * subdivide_step); m_ddfx = tmpx * FLT_TO_SCALAR(2.0f); m_ddfy = tmpy * FLT_TO_SCALAR(2.0f); m_step = m_num_steps; } void curve3_inc::rewind(unsigned int) { if (m_num_steps == 0) { m_step = -1; return; } m_step = m_num_steps; m_fx = m_saved_fx; m_fy = m_saved_fy; m_dfx = m_saved_dfx; m_dfy = m_saved_dfy; } unsigned int curve3_inc::vertex(scalar* x, scalar* y) { if (m_step < 0) return path_cmd_stop; if (m_step == m_num_steps) { *x = m_start_x; *y = m_start_y; --m_step; return path_cmd_move_to; } if (m_step == 0) { *x = m_end_x; *y = m_end_y; --m_step; return path_cmd_line_to; } m_fx += m_dfx; m_fy += m_dfy; m_dfx += m_ddfx; m_dfy += m_ddfy; *x = m_fx; *y = m_fy; --m_step; return path_cmd_line_to; } //curve3_div void curve3_div::init(scalar x1, scalar y1, scalar x2, scalar y2, scalar x3, scalar y3) { m_points.clear(); m_distance_tolerance_square = FLT_TO_SCALAR(0.5f) / m_approximation_scale; m_distance_tolerance_square *= m_distance_tolerance_square; bezier(x1, y1, x2, y2, x3, y3); m_count = 0; } //------------------------------------------------------------------------ void curve3_div::recursive_bezier(scalar x1, scalar y1, scalar x2, scalar y2, scalar x3, scalar y3, unsigned int level) { if (level > curve_recursion_limit) { return; } // Calculate all the mid-points of the line segments //---------------------- scalar x12 = (x1 + x2) / 2; scalar y12 = (y1 + y2) / 2; scalar x23 = (x2 + x3) / 2; scalar y23 = (y2 + y3) / 2; scalar x123 = (x12 + x23) / 2; scalar y123 = (y12 + y23) / 2; scalar dx = x3-x1; scalar dy = y3-y1; scalar d = Fabs(((x2 - x3) * dy - (y2 - y3) * dx)); scalar da; if (d > curve_collinearity_epsilon) { // Regular case //----------------- if (d * d <= m_distance_tolerance_square * (dx*dx + dy*dy)) { // If the curvature doesn't exceed the distance_tolerance value // we tend to finish subdivisions. //---------------------- if (m_angle_tolerance < curve_angle_tolerance_epsilon) { m_points.add(vertex_s(x123, y123)); return; } // Angle & Cusp Condition //---------------------- da = Fabs(Atan2(y3 - y2, x3 - x2) - Atan2(y2 - y1, x2 - x1)); if (da >= PI) da = _2PI - da; if (da < m_angle_tolerance) { // Finally we can stop the recursion //---------------------- m_points.add(vertex_s(x123, y123)); return; } } } else { // Collinear case //------------------ da = dx*dx + dy*dy; if (da == 0) { d = calc_sq_distance(x1, y1, x2, y2); } else { d = ((x2 - x1)*dx + (y2 - y1)*dy) / da; if (d > 0 && d < 1) { // Simple collinear case, 1---2---3 // We can leave just two endpoints return; } if (d <= 0) d = calc_sq_distance(x2, y2, x1, y1); else if (d >= 1) d = calc_sq_distance(x2, y2, x3, y3); else d = calc_sq_distance(x2, y2, x1 + d*dx, y1 + d*dy); } if (d < m_distance_tolerance_square) { m_points.add(vertex_s(x2, y2)); return; } } // Continue subdivision //---------------------- recursive_bezier(x1, y1, x12, y12, x123, y123, level + 1); recursive_bezier(x123, y123, x23, y23, x3, y3, level + 1); } //------------------------------------------------------------------------ void curve3_div::bezier(scalar x1, scalar y1, scalar x2, scalar y2, scalar x3, scalar y3) { m_points.add(vertex_s(x1, y1)); recursive_bezier(x1, y1, x2, y2, x3, y3, 0); m_points.add(vertex_s(x3, y3)); } //curve4_inc void curve4_inc::init(scalar x1, scalar y1, scalar x2, scalar y2, scalar x3, scalar y3, scalar x4, scalar y4) { m_start_x = x1; m_start_y = y1; m_end_x = x4; m_end_y = y4; scalar dx1 = x2 - x1; scalar dy1 = y2 - y1; scalar dx2 = x3 - x2; scalar dy2 = y3 - y2; scalar dx3 = x4 - x3; scalar dy3 = y4 - y3; scalar len = (Sqrt(dx1 * dx1 + dy1 * dy1) + Sqrt(dx2 * dx2 + dy2 * dy2) + Sqrt(dx3 * dx3 + dy3 * dy3)) * FLT_TO_SCALAR(0.25f) * m_scale; m_num_steps = uround(len); if (m_num_steps < 4) { m_num_steps = 4; } scalar subdivide_step = FLT_TO_SCALAR(1.0f) / m_num_steps; scalar subdivide_step2 = subdivide_step * subdivide_step; scalar subdivide_step3 = subdivide_step * subdivide_step * subdivide_step; scalar pre1 = FLT_TO_SCALAR(3.0f) * subdivide_step; scalar pre2 = FLT_TO_SCALAR(3.0f) * subdivide_step2; scalar pre4 = FLT_TO_SCALAR(6.0f) * subdivide_step2; scalar pre5 = FLT_TO_SCALAR(6.0f) * subdivide_step3; scalar tmp1x = x1 - x2 * FLT_TO_SCALAR(2.0f) + x3; scalar tmp1y = y1 - y2 * FLT_TO_SCALAR(2.0f) + y3; scalar tmp2x = (x2 - x3) * FLT_TO_SCALAR(3.0f) - x1 + x4; scalar tmp2y = (y2 - y3) * FLT_TO_SCALAR(3.0f) - y1 + y4; m_saved_fx = m_fx = x1; m_saved_fy = m_fy = y1; m_saved_dfx = m_dfx = (x2 - x1) * pre1 + tmp1x * pre2 + tmp2x * subdivide_step3; m_saved_dfy = m_dfy = (y2 - y1) * pre1 + tmp1y * pre2 + tmp2y * subdivide_step3; m_saved_ddfx = m_ddfx = tmp1x * pre4 + tmp2x * pre5; m_saved_ddfy = m_ddfy = tmp1y * pre4 + tmp2y * pre5; m_dddfx = tmp2x * pre5; m_dddfy = tmp2y * pre5; m_step = m_num_steps; } void curve4_inc::rewind(unsigned int) { if (m_num_steps == 0) { m_step = -1; return; } m_step = m_num_steps; m_fx = m_saved_fx; m_fy = m_saved_fy; m_dfx = m_saved_dfx; m_dfy = m_saved_dfy; m_ddfx = m_saved_ddfx; m_ddfy = m_saved_ddfy; } unsigned int curve4_inc::vertex(scalar* x, scalar* y) { if (m_step < 0) return path_cmd_stop; if (m_step == m_num_steps) { *x = m_start_x; *y = m_start_y; --m_step; return path_cmd_move_to; } if (m_step == 0) { *x = m_end_x; *y = m_end_y; --m_step; return path_cmd_line_to; } m_fx += m_dfx; m_fy += m_dfy; m_dfx += m_ddfx; m_dfy += m_ddfy; m_ddfx += m_dddfx; m_ddfy += m_dddfy; *x = m_fx; *y = m_fy; --m_step; return path_cmd_line_to; } //curve4_div void curve4_div::init(scalar x1, scalar y1, scalar x2, scalar y2, scalar x3, scalar y3, scalar x4, scalar y4) { m_points.clear(); m_distance_tolerance_square = FLT_TO_SCALAR(0.5f) / m_approximation_scale; m_distance_tolerance_square *= m_distance_tolerance_square; bezier(x1, y1, x2, y2, x3, y3, x4, y4); m_count = 0; } void curve4_div::recursive_bezier(scalar x1, scalar y1, scalar x2, scalar y2, scalar x3, scalar y3, scalar x4, scalar y4, unsigned int level) { if (level > curve_recursion_limit) { return; } // Calculate all the mid-points of the line segments //---------------------- scalar x12 = (x1 + x2) / 2; scalar y12 = (y1 + y2) / 2; scalar x23 = (x2 + x3) / 2; scalar y23 = (y2 + y3) / 2; scalar x34 = (x3 + x4) / 2; scalar y34 = (y3 + y4) / 2; scalar x123 = (x12 + x23) / 2; scalar y123 = (y12 + y23) / 2; scalar x234 = (x23 + x34) / 2; scalar y234 = (y23 + y34) / 2; scalar x1234 = (x123 + x234) / 2; scalar y1234 = (y123 + y234) / 2; // Try to approximate the full cubic curve by a single straight line //------------------ scalar dx = x4-x1; scalar dy = y4-y1; scalar d2 = Fabs(((x2 - x4) * dy - (y2 - y4) * dx)); scalar d3 = Fabs(((x3 - x4) * dy - (y3 - y4) * dx)); scalar da1, da2, k; switch((int(d2 > curve_collinearity_epsilon) << 1) + int(d3 > curve_collinearity_epsilon)) { case 0: // All collinear OR p1==p4 //---------------------- k = dx*dx + dy*dy; if (k == 0) { d2 = calc_sq_distance(x1, y1, x2, y2); d3 = calc_sq_distance(x4, y4, x3, y3); } else { k = 1 / k; da1 = x2 - x1; da2 = y2 - y1; d2 = k * (da1*dx + da2*dy); da1 = x3 - x1; da2 = y3 - y1; d3 = k * (da1*dx + da2*dy); if (d2 > 0 && d2 < 1 && d3 > 0 && d3 < 1) { // Simple collinear case, 1---2---3---4 // We can leave just two endpoints return; } if (d2 <= 0) d2 = calc_sq_distance(x2, y2, x1, y1); else if(d2 >= 1) d2 = calc_sq_distance(x2, y2, x4, y4); else d2 = calc_sq_distance(x2, y2, x1 + d2*dx, y1 + d2*dy); if (d3 <= 0) d3 = calc_sq_distance(x3, y3, x1, y1); else if(d3 >= 1) d3 = calc_sq_distance(x3, y3, x4, y4); else d3 = calc_sq_distance(x3, y3, x1 + d3*dx, y1 + d3*dy); } if (d2 > d3) { if (d2 < m_distance_tolerance_square) { m_points.add(vertex_s(x2, y2)); return; } } else { if (d3 < m_distance_tolerance_square) { m_points.add(vertex_s(x3, y3)); return; } } break; case 1: // p1,p2,p4 are collinear, p3 is significant //---------------------- if (d3 * d3 <= m_distance_tolerance_square * (dx*dx + dy*dy)) { if (m_angle_tolerance < curve_angle_tolerance_epsilon) { m_points.add(vertex_s(x23, y23)); return; } // Angle Condition //---------------------- da1 = Fabs(Atan2(y4 - y3, x4 - x3) - Atan2(y3 - y2, x3 - x2)); if (da1 >= PI) da1 = _2PI - da1; if (da1 < m_angle_tolerance) { m_points.add(vertex_s(x2, y2)); m_points.add(vertex_s(x3, y3)); return; } if (m_cusp_limit != 0.0) { if (da1 > m_cusp_limit) { m_points.add(vertex_s(x3, y3)); return; } } } break; case 2: // p1,p3,p4 are collinear, p2 is significant //---------------------- if (d2 * d2 <= m_distance_tolerance_square * (dx*dx + dy*dy)) { if (m_angle_tolerance < curve_angle_tolerance_epsilon) { m_points.add(vertex_s(x23, y23)); return; } // Angle Condition //---------------------- da1 = Fabs(Atan2(y3 - y2, x3 - x2) - Atan2(y2 - y1, x2 - x1)); if (da1 >= PI) da1 = _2PI - da1; if (da1 < m_angle_tolerance) { m_points.add(vertex_s(x2, y2)); m_points.add(vertex_s(x3, y3)); return; } if (m_cusp_limit != 0.0) { if (da1 > m_cusp_limit) { m_points.add(vertex_s(x2, y2)); return; } } } break; case 3: // Regular case //----------------- if ((d2 + d3)*(d2 + d3) <= m_distance_tolerance_square * (dx*dx + dy*dy)) { // If the curvature doesn't exceed the distance_tolerance value // we tend to finish subdivisions. //---------------------- if (m_angle_tolerance < curve_angle_tolerance_epsilon) { m_points.add(vertex_s(x23, y23)); return; } // Angle & Cusp Condition //---------------------- k = Atan2(y3 - y2, x3 - x2); da1 = Fabs(k - Atan2(y2 - y1, x2 - x1)); da2 = Fabs(Atan2(y4 - y3, x4 - x3) - k); if (da1 >= PI) da1 = _2PI - da1; if (da2 >= PI) da2 = _2PI - da2; if (da1 + da2 < m_angle_tolerance) { // Finally we can stop the recursion //---------------------- m_points.add(vertex_s(x23, y23)); return; } if (m_cusp_limit != 0.0) { if (da1 > m_cusp_limit) { m_points.add(vertex_s(x2, y2)); return; } if (da2 > m_cusp_limit) { m_points.add(vertex_s(x3, y3)); return; } } } break; } // Continue subdivision //---------------------- recursive_bezier(x1, y1, x12, y12, x123, y123, x1234, y1234, level + 1); recursive_bezier(x1234, y1234, x234, y234, x34, y34, x4, y4, level + 1); } void curve4_div::bezier(scalar x1, scalar y1, scalar x2, scalar y2, scalar x3, scalar y3, scalar x4, scalar y4) { m_points.add(vertex_s(x1, y1)); recursive_bezier(x1, y1, x2, y2, x3, y3, x4, y4, 0); m_points.add(vertex_s(x4, y4)); } }
bool gfx_font_adapter::prepare_glyph(unsigned int code) { if (m_impl->font) { m_impl->cur_glyph_index = FT_Get_Char_Index(m_impl->font, code); int error = FT_Load_Glyph(m_impl->font, m_impl->cur_glyph_index, m_impl->hinting ? FT_LOAD_DEFAULT : FT_LOAD_NO_HINTING); bool is_sys_bitmap = false; if (m_impl->font->glyph->format == FT_GLYPH_FORMAT_BITMAP) is_sys_bitmap = true; if (error == 0) { if (m_impl->antialias && !is_sys_bitmap) { if (m_impl->weight == 500) { int strength = 1 << 5; FT_Outline_Embolden(&(m_impl->font->glyph->outline), strength); } else if (m_impl->weight == 700) { int strength = 1 << 6; FT_Outline_Embolden(&(m_impl->font->glyph->outline), strength); } else if (m_impl->weight == 900) { int strength = 1 << 7; FT_Outline_Embolden(&(m_impl->font->glyph->outline), strength); } // outline text m_impl->cur_data_type = glyph_type_outline; m_impl->cur_font_path.remove_all(); if (decompose_ft_outline(m_impl->font->glyph->outline, m_impl->flip_y, m_impl->matrix, m_impl->cur_font_path)) { m_impl->cur_bound_rect = get_bounding_rect(m_impl->cur_font_path); m_impl->cur_data_size = m_impl->cur_font_path.total_byte_size()+sizeof(unsigned int);//count data m_impl->cur_advance_x = FLT_TO_SCALAR(int26p6_to_flt(m_impl->font->glyph->advance.x)); m_impl->cur_advance_y = FLT_TO_SCALAR(int26p6_to_flt(m_impl->font->glyph->advance.y)); m_impl->matrix.transform(&m_impl->cur_advance_x, &m_impl->cur_advance_y); return true; } } else { m_impl->cur_data_type = glyph_type_mono; if (is_sys_bitmap || !FT_IS_SCALABLE(m_impl->font) || m_impl->matrix.is_identity()) { gfx_scanline_bin sl; error = FT_Render_Glyph(m_impl->font->glyph, FT_RENDER_MODE_MONO); if (error == 0) { decompose_ft_bitmap_mono(m_impl->font->glyph->bitmap, m_impl->font->glyph->bitmap_left, m_impl->flip_y ? -m_impl->font->glyph->bitmap_top : m_impl->font->glyph->bitmap_top, m_impl->flip_y, sl, m_impl->cur_font_scanlines_bin); m_impl->cur_bound_rect = rect(m_impl->cur_font_scanlines_bin.min_x(), m_impl->cur_font_scanlines_bin.min_y(), m_impl->cur_font_scanlines_bin.max_x() + 1, m_impl->cur_font_scanlines_bin.max_y() + 1); m_impl->cur_data_size = m_impl->cur_font_scanlines_bin.byte_size(); m_impl->cur_advance_x = FLT_TO_SCALAR(int26p6_to_flt(m_impl->font->glyph->advance.x)); m_impl->cur_advance_y = FLT_TO_SCALAR(int26p6_to_flt(m_impl->font->glyph->advance.y)); return true; } } else { if (m_impl->weight == 500) { int strength = 1 << 5; FT_Outline_Embolden(&(m_impl->font->glyph->outline), strength); } else if (m_impl->weight == 700) { int strength = 1 << 6; FT_Outline_Embolden(&(m_impl->font->glyph->outline), strength); } else if (m_impl->weight == 900) { int strength = 1 << 7; FT_Outline_Embolden(&(m_impl->font->glyph->outline), strength); } m_impl->cur_font_path.remove_all(); if (decompose_ft_outline(m_impl->font->glyph->outline, m_impl->flip_y, m_impl->matrix, m_impl->cur_font_path)) { gfx_rasterizer_scanline_aa<> rasterizer; picasso::conv_curve curves(m_impl->cur_font_path); curves.approximation_scale(4.0); rasterizer.add_path(curves); gfx_scanline_bin sl; m_impl->cur_font_scanlines_bin.prepare(); // Remove all gfx_render_scanlines(rasterizer, sl, m_impl->cur_font_scanlines_bin); m_impl->cur_bound_rect = rect(m_impl->cur_font_scanlines_bin.min_x(), m_impl->cur_font_scanlines_bin.min_y(), m_impl->cur_font_scanlines_bin.max_x() + 1, m_impl->cur_font_scanlines_bin.max_y() + 1); m_impl->cur_data_size = m_impl->cur_font_scanlines_bin.byte_size(); m_impl->cur_advance_x = FLT_TO_SCALAR(int26p6_to_flt(m_impl->font->glyph->advance.x)); m_impl->cur_advance_y = FLT_TO_SCALAR(int26p6_to_flt(m_impl->font->glyph->advance.y)); m_impl->matrix.transform(&m_impl->cur_advance_x, &m_impl->cur_advance_y); return true; } } } } } return false; }
static bool decompose_ft_outline(const FT_Outline& outline, bool flip_y, const gfx_trans_affine& mtx, graphic_path& path) { FT_Vector v_last; FT_Vector v_control; FT_Vector v_start; float x1, y1, x2, y2, x3, y3; FT_Vector* point; FT_Vector* limit; char* tags; int first; // index of first point in contour char tag; // current point's state first = 0; for (int n = 0; n < outline.n_contours; n++) { int last; // index of last point in contour last = outline.contours[n]; limit = outline.points + last; v_start = outline.points[first]; v_last = outline.points[last]; v_control = v_start; point = outline.points + first; tags = outline.tags + first; tag = FT_CURVE_TAG(tags[0]); // A contour cannot start with a cubic control point! if (tag == FT_CURVE_TAG_CUBIC) return false; // check first point to determine origin if (tag == FT_CURVE_TAG_CONIC) { // first point is conic control. if (FT_CURVE_TAG(outline.tags[last]) == FT_CURVE_TAG_ON) { // start at last point if it is on the curve v_start = v_last; limit--; } else { // if both first and last points are conic, // start at their middle and record its position // for closure v_start.x = (v_start.x + v_last.x) / 2; v_start.y = (v_start.y + v_last.y) / 2; v_last = v_start; } point--; tags--; } x1 = int26p6_to_flt(v_start.x); y1 = int26p6_to_flt(v_start.y); if (flip_y) y1 = -y1; mtx.transform(&x1, &y1); path.move_to(FLT_TO_SCALAR(x1), FLT_TO_SCALAR(y1)); while (point < limit) { point++; tags++; tag = FT_CURVE_TAG(tags[0]); switch (tag) { case FT_CURVE_TAG_ON: // emit a single line_to { x1 = int26p6_to_flt(point->x); y1 = int26p6_to_flt(point->y); if (flip_y) y1 = -y1; mtx.transform(&x1, &y1); path.line_to(FLT_TO_SCALAR(x1), FLT_TO_SCALAR(y1)); continue; } case FT_CURVE_TAG_CONIC: // consume conic arcs { v_control.x = point->x; v_control.y = point->y; Do_Conic: if (point < limit) { FT_Vector vec; FT_Vector v_middle; point++; tags++; tag = FT_CURVE_TAG(tags[0]); vec.x = point->x; vec.y = point->y; if (tag == FT_CURVE_TAG_ON) { x1 = int26p6_to_flt(v_control.x); y1 = int26p6_to_flt(v_control.y); x2 = int26p6_to_flt(vec.x); y2 = int26p6_to_flt(vec.y); if (flip_y) { y1 = -y1; y2 = -y2; } mtx.transform(&x1, &y1); mtx.transform(&x2, &y2); path.curve3(FLT_TO_SCALAR(x1), FLT_TO_SCALAR(y1), FLT_TO_SCALAR(x2), FLT_TO_SCALAR(y2)); continue; } if (tag != FT_CURVE_TAG_CONIC) return false; v_middle.x = (v_control.x + vec.x) / 2; v_middle.y = (v_control.y + vec.y) / 2; x1 = int26p6_to_flt(v_control.x); y1 = int26p6_to_flt(v_control.y); x2 = int26p6_to_flt(v_middle.x); y2 = int26p6_to_flt(v_middle.y); if (flip_y) { y1 = -y1; y2 = -y2; } mtx.transform(&x1, &y1); mtx.transform(&x2, &y2); path.curve3(FLT_TO_SCALAR(x1), FLT_TO_SCALAR(y1), FLT_TO_SCALAR(x2), FLT_TO_SCALAR(y2)); v_control = vec; goto Do_Conic; } x1 = int26p6_to_flt(v_control.x); y1 = int26p6_to_flt(v_control.y); x2 = int26p6_to_flt(v_start.x); y2 = int26p6_to_flt(v_start.y); if (flip_y) { y1 = -y1; y2 = -y2; } mtx.transform(&x1, &y1); mtx.transform(&x2, &y2); path.curve3(FLT_TO_SCALAR(x1), FLT_TO_SCALAR(y1), FLT_TO_SCALAR(x2), FLT_TO_SCALAR(y2)); goto Close; } default: // FT_CURVE_TAG_CUBIC { FT_Vector vec1, vec2; if (point + 1 > limit || FT_CURVE_TAG(tags[1]) != FT_CURVE_TAG_CUBIC) { return false; } vec1.x = point[0].x; vec1.y = point[0].y; vec2.x = point[1].x; vec2.y = point[1].y; point += 2; tags += 2; if (point <= limit) { FT_Vector vec; vec.x = point->x; vec.y = point->y; x1 = int26p6_to_flt(vec1.x); y1 = int26p6_to_flt(vec1.y); x2 = int26p6_to_flt(vec2.x); y2 = int26p6_to_flt(vec2.y); x3 = int26p6_to_flt(vec.x); y3 = int26p6_to_flt(vec.y); if (flip_y) { y1 = -y1; y2 = -y2; y3 = -y3; } mtx.transform(&x1, &y1); mtx.transform(&x2, &y2); mtx.transform(&x3, &y3); path.curve4(FLT_TO_SCALAR(x1), FLT_TO_SCALAR(y1), FLT_TO_SCALAR(x2), FLT_TO_SCALAR(y2), FLT_TO_SCALAR(x3), FLT_TO_SCALAR(y3)); continue; } x1 = int26p6_to_flt(vec1.x); y1 = int26p6_to_flt(vec1.y); x2 = int26p6_to_flt(vec2.x); y2 = int26p6_to_flt(vec2.y); x3 = int26p6_to_flt(v_start.x); y3 = int26p6_to_flt(v_start.y); if (flip_y) { y1 = -y1; y2 = -y2; y3 = -y3; } mtx.transform(&x1, &y1); mtx.transform(&x2, &y2); mtx.transform(&x3, &y3); path.curve4(FLT_TO_SCALAR(x1), FLT_TO_SCALAR(y1), FLT_TO_SCALAR(x2), FLT_TO_SCALAR(y2), FLT_TO_SCALAR(x3), FLT_TO_SCALAR(y3)); goto Close; } } } path.close_polygon(); Close: first = last + 1; } return true; }
void PICAPI ps_path_add_rounded_rect(ps_path*path, const ps_rect* r, float ltx, float lty, float rtx, float rty, float lbx, float lby, float rbx, float rby) { if (!picasso::is_valid_system_device()) { global_status = STATUS_DEVICE_ERROR; return; } if (!path || !r) { global_status = STATUS_INVALID_ARGUMENT; return; } picasso::rounded_rect rr; rr.rect(FLT_TO_SCALAR(r->x), FLT_TO_SCALAR(r->y), FLT_TO_SCALAR(r->x+r->w), FLT_TO_SCALAR(r->y+r->h)); rr.radius(FLT_TO_SCALAR(ltx), FLT_TO_SCALAR(lty), FLT_TO_SCALAR(rtx), FLT_TO_SCALAR(rty), FLT_TO_SCALAR(lbx), FLT_TO_SCALAR(lby), FLT_TO_SCALAR(rbx), FLT_TO_SCALAR(rby)); rr.normalize_radius(); if (picasso::_is_closed_path(path->path)) path->path.concat_path(rr, 0); else path->path.join_path(rr, 0); global_status = STATUS_SUCCEED; }
void PICAPI ps_draw_text(ps_context* ctx, const ps_rect* area, const void* text, unsigned int len, ps_draw_text_type type, ps_text_align align) { if (!picasso::is_valid_system_device()) { global_status = STATUS_DEVICE_ERROR; return; } if (!ctx || !area || !text || !len) { global_status = STATUS_INVALID_ARGUMENT; return; } scalar x = FLT_TO_SCALAR(area->x); scalar y = FLT_TO_SCALAR(area->y); picasso::graphic_path text_path; ps_bool text_antialias = ctx->font_antialias; ctx->font_antialias = True; if (create_device_font(ctx)) { // align layout scalar w = 0, h = 0; const picasso::glyph* glyph_test = 0; if (ctx->state->font->desc.charset() == CHARSET_ANSI) { const char* p = (const char*)text; glyph_test = ctx->fonts->current_font()->get_glyph(*p); } else { const ps_uchar16* p = (const ps_uchar16*)text; glyph_test = ctx->fonts->current_font()->get_glyph(*p); } if (glyph_test) { w = glyph_test->advance_x; h = glyph_test->height; //Note: advance_y always 0. } w *= len; //FIXME: estimate! if (align & TEXT_ALIGN_LEFT) x = FLT_TO_SCALAR(area->x); else if (align & TEXT_ALIGN_RIGHT) x = FLT_TO_SCALAR(area->x + (area->w - w)); else x = FLT_TO_SCALAR(area->x + (area->w - w)/2); if (align & TEXT_ALIGN_TOP) { y = FLT_TO_SCALAR(area->y); y += ctx->fonts->current_font()->ascent(); } else if (align & TEXT_ALIGN_BOTTOM) { y = FLT_TO_SCALAR(area->y + (area->h - SCALAR_TO_FLT(h))); y -= ctx->fonts->current_font()->descent(); } else { y = FLT_TO_SCALAR(area->y + (area->h - SCALAR_TO_FLT(h))/2); y += (ctx->fonts->current_font()->ascent() - ctx->fonts->current_font()->descent())/2; } // draw the text if (ctx->state->font->desc.charset() == CHARSET_ANSI) { const char* p = (const char*)text; while (*p && len) { register char c = *p; const picasso::glyph* glyph = ctx->fonts->current_font()->get_glyph(c); if (glyph) { if (ctx->font_kerning) ctx->fonts->current_font()->add_kerning(&x, &y); if (ctx->fonts->current_font()->generate_raster(glyph, x, y)) _add_glyph_to_path(ctx, text_path); x += glyph->advance_x; y += glyph->advance_y; } len--; p++; } } else { const ps_uchar16* p = (const ps_uchar16*)text; while (*p && len) { register ps_uchar16 c = *p; const picasso::glyph* glyph = ctx->fonts->current_font()->get_glyph(c); if (glyph) { if (ctx->font_kerning) ctx->fonts->current_font()->add_kerning(&x, &y); if (ctx->fonts->current_font()->generate_raster(glyph, x, y)) _add_glyph_to_path(ctx, text_path); x += glyph->advance_x; y += glyph->advance_y; } len--; p++; } } } text_path.close_polygon(); ctx->font_antialias = text_antialias; //store the old color picasso::rgba bc = ctx->state->brush.color; picasso::rgba pc = ctx->state->pen.color; ctx->state->brush.color = ctx->state->font_fcolor; ctx->state->pen.color = ctx->state->font_scolor; switch (type) { case DRAW_TEXT_FILL: ctx->canvas->p->render_shadow(ctx->state, text_path, true, false); ctx->canvas->p->render_fill(ctx->state, ctx->raster, text_path); ctx->canvas->p->render_blur(ctx->state); break; case DRAW_TEXT_STROKE: ctx->canvas->p->render_shadow(ctx->state, text_path, false, true); ctx->canvas->p->render_stroke(ctx->state, ctx->raster, text_path); ctx->canvas->p->render_blur(ctx->state); break; case DRAW_TEXT_BOTH: ctx->canvas->p->render_shadow(ctx->state, text_path, true, true); ctx->canvas->p->render_paint(ctx->state, ctx->raster, text_path); ctx->canvas->p->render_blur(ctx->state); break; } ctx->state->brush.color = bc; ctx->state->pen.color = pc; ctx->raster.reset(); global_status = STATUS_SUCCEED; }
scalar radius(void) const { return FLT_TO_SCALAR(2.0f); }