svg_status_t _svg_android_render_line (void *closure, svg_length_t *x1_len, svg_length_t *y1_len, svg_length_t *x2_len, svg_length_t *y2_len) { svg_android_t *svg_android = closure; svg_status_t status; double x1, y1, x2, y2; DEBUG_ENTRY("render_line"); _svg_android_length_to_pixel (svg_android, x1_len, &x1); _svg_android_length_to_pixel (svg_android, y1_len, &y1); _svg_android_length_to_pixel (svg_android, x2_len, &x2); _svg_android_length_to_pixel (svg_android, y2_len, &y2); status = _svg_android_move_to (svg_android, x1, y1); if (status) return status; status = _svg_android_line_to (svg_android, x2, y2); if (status) return status; status = _svg_android_render_path (svg_android); if (status) return status; DEBUG_EXIT("render_line"); return SVG_ANDROID_STATUS_SUCCESS; }
svg_status_t _svg_android_set_pattern (svg_android_t *svg_android, svg_element_t *pattern_element, svg_android_render_type_t type) { svg_pattern_t *pattern = svg_element_pattern (pattern_element); jobject pattern_bitmap; jobject pattern_shader; double x_px, y_px, width_px, height_px; jobject path; _svg_android_length_to_pixel (svg_android, &pattern->x, &x_px); _svg_android_length_to_pixel (svg_android, &pattern->y, &y_px); _svg_android_length_to_pixel (svg_android, &pattern->width, &width_px); _svg_android_length_to_pixel (svg_android, &pattern->height, &height_px); /* OK. We've got the final path to be filled/stroked inside the * android context right now. But we're also going to re-use that * same context to draw the pattern. And since the path is no * longer in the graphics state, android_save/restore will not help * us here. * * Currently we deal with this by manually saving/restoring the * path. * */ path = svg_android->state->path; svg_android->state->path = ANDROID_PATH_CREATE(svg_android); ANDROID_SAVE(svg_android); pattern_bitmap = ANDROID_CREATE_BITMAP(svg_android, (int) (width_px + 0.5), (int) (height_px + 0.5)); _svg_android_push_state (svg_android, pattern_bitmap, NULL); svg_android->state->matrix = ANDROID_IDENTITY_MATRIX(svg_android); svg_android->state->fill_paint.type = SVG_PAINT_TYPE_NONE; svg_android->state->stroke_paint.type = SVG_PAINT_TYPE_NONE; svg_element_render (pattern->group_element, &SVG_ANDROID_RENDER_ENGINE, svg_android); _svg_android_pop_state (svg_android); ANDROID_RESTORE(svg_android); svg_android->state->path = path ; pattern_shader = ANDROID_CREATE_BITMAP_SHADER(svg_android, pattern_bitmap); ANDROID_PAINT_SET_SHADER(svg_android, pattern_shader); return SVG_STATUS_SUCCESS; }
svg_status_t _svg_android_render_text (void *closure, svg_length_t *x_len, svg_length_t *y_len, const char *utf8) { svg_android_t *svg_android = closure; double x, y; svg_status_t status; svg_paint_t *fill_paint, *stroke_paint; DEBUG_ENTRY("render_text"); fill_paint = &svg_android->state->fill_paint; stroke_paint = &svg_android->state->stroke_paint; _svg_android_select_font (svg_android); _svg_android_length_to_pixel (svg_android, x_len, &x); _svg_android_length_to_pixel (svg_android, y_len, &y); status = _svg_android_move_to (svg_android, x, y); if (status) return status; ANDROID_TEXT_PATH(svg_android, utf8, x, y); _svg_android_render_path (svg_android); #if 0 // this code doesn't work with renderToArea if (fill_paint->type) { _svg_android_set_paint_and_opacity (svg_android, fill_paint, svg_android->state->fill_opacity, SVG_ANDROID_RENDER_TYPE_FILL); // ANDROID_DRAW_TEXT(svg_android, utf8, 0.0, 0.0); ANDROID_DRAW_TEXT(svg_android, utf8, x, y); } if (stroke_paint->type) { _svg_android_set_paint_and_opacity (svg_android, stroke_paint, svg_android->state->stroke_opacity, SVG_ANDROID_RENDER_TYPE_STROKE); ANDROID_DRAW_TEXT(svg_android, utf8, x, y); } #endif DEBUG_EXIT("render_text"); return SVG_ANDROID_STATUS_SUCCESS; }
svg_status_t _svg_android_set_viewport_dimension (void *closure, svg_length_t *width, svg_length_t *height) { svg_android_t *svg_android = closure; double vwidth, vheight; DEBUG_ENTRY("set_viewpoert_dimension"); _svg_android_length_to_pixel (svg_android, width, &vwidth); _svg_android_length_to_pixel (svg_android, height, &vheight); svg_android->state->viewport_width = vwidth; svg_android->state->viewport_height = vheight; if(svg_android->fit_to_area) { // calculate fit_to_MATRIX values double xx, x0, w; double yy, y0, h; w = (double)(svg_android->fit_to_w); h = (double)(svg_android->fit_to_h); xx = w / vwidth; yy = h / vheight; // Do equal scaling if needed... if (svg_android->fit_uniform) { if(xx < yy) yy = xx; if(xx > yy) xx = yy; } svg_android->fit_to_scale = xx; x0 = (double)(svg_android->fit_to_x); y0 = (double)(svg_android->fit_to_y); svg_android->fit_to_MATRIX = ANDROID_MATRIX_INIT(svg_android, xx, 0.0, 0.0, yy, x0, y0); } else svg_android->fit_to_scale = 1.0; DEBUG_EXIT("set_viewpoert_dimension"); return SVG_ANDROID_STATUS_SUCCESS; }
svg_status_t _svg_android_render_image (void *closure, unsigned char *data, unsigned int data_width, unsigned int data_height, svg_length_t *x_len, svg_length_t *y_len, svg_length_t *width_len, svg_length_t *height_len) { svg_android_t *svg_android = closure; double x, y, width, height; jintArray iarr; jobject bitmap; jobject matrix; DEBUG_ENTRY("render_image"); ANDROID_SAVE(svg_android); _svg_android_length_to_pixel (svg_android, x_len, &x); _svg_android_length_to_pixel (svg_android, y_len, &y); _svg_android_length_to_pixel (svg_android, width_len, &width); _svg_android_length_to_pixel (svg_android, height_len, &height); // copy bitmap into an java int array iarr = (*(svg_android->env))->NewIntArray(svg_android->env, data_width * data_height); (*(svg_android->env))->SetIntArrayRegion(svg_android->env, iarr, 0, data_width * data_height , (jint *)data); // create bitmap bitmap = ANDROID_DATA_2_BITMAP(svg_android, iarr, data_width, data_height); // prepare matrix matrix = ANDROID_IDENTITY_MATRIX(svg_android); ANDROID_MATRIX_TRANSLATE(svg_android, matrix, x, y); ANDROID_MATRIX_SCALE(svg_android, matrix, width / data_width, height / data_height); // and draw! ANDROID_DRAW_BITMAP(svg_android, bitmap, matrix); ANDROID_RESTORE(svg_android); DEBUG_EXIT("render_image"); return SVG_ANDROID_STATUS_SUCCESS; }
svg_status_t _svg_android_set_stroke_dash_offset (void *closure, svg_length_t *offset_len) { svg_android_t *svg_android = closure; double offset; DEBUG_ENTRY("set_stroke_dash_offset"); _svg_android_length_to_pixel (svg_android, offset_len, &offset); svg_android->state->dash_offset = offset; if (svg_android->state->num_dashes) { jfloatArray farr; // prepare Android float array with dashes, make sure the array is even in length.. { int max_k; int k; jfloat *buf; max_k = svg_android->state->num_dashes; if(max_k & 0x1) max_k++; // make even buf = (jfloat *)malloc(sizeof(jfloat) * max_k); if(buf == NULL) return SVG_STATUS_NO_MEMORY; farr = (*(svg_android->env))->NewFloatArray(svg_android->env, max_k); if (farr == NULL) { free(buf); return SVG_ANDROID_STATUS_NO_MEMORY; /* out of memory error thrown */ } for(k = 0; k < svg_android->state->num_dashes; k++) { buf[k] = svg_android->state->dash[k]; } for(; k < max_k; k++) buf[k] = 0.0; (*(svg_android->env))->SetFloatArrayRegion(svg_android->env, farr, 0, max_k, buf); free(buf); } jobject effect = ANDROID_GET_DASHEFFECT( svg_android, farr, svg_android->state->dash_offset); ANDROID_PAINT_SET_EFFECT(svg_android, effect); } DEBUG_EXIT("set_stroke_dash_offset"); return SVG_ANDROID_STATUS_SUCCESS; }
svg_status_t _svg_android_render_ellipse (void *closure, svg_length_t *cx_len, svg_length_t *cy_len, svg_length_t *rx_len, svg_length_t *ry_len) { svg_android_t *svg_android = closure; double cx, cy, rx, ry; DEBUG_ENTRY("render_ellipse"); _svg_android_length_to_pixel (svg_android, cx_len, &cx); _svg_android_length_to_pixel (svg_android, cy_len, &cy); _svg_android_length_to_pixel (svg_android, rx_len, &rx); _svg_android_length_to_pixel (svg_android, ry_len, &ry); ANDROID_DRAW_ELLIPSE(svg_android, cx, cy, rx, ry); DEBUG_EXIT("render_ellipse"); return SVG_ANDROID_STATUS_SUCCESS; }
svg_status_t _svg_android_render_rect (void *closure, svg_length_t *x_len, svg_length_t *y_len, svg_length_t *width_len, svg_length_t *height_len, svg_length_t *rx_len, svg_length_t *ry_len) { svg_android_t *svg_android = closure; double x, y, width, height, rx, ry; DEBUG_ENTRY("render_rect"); _svg_android_length_to_pixel (svg_android, x_len, &x); _svg_android_length_to_pixel (svg_android, y_len, &y); _svg_android_length_to_pixel (svg_android, width_len, &width); _svg_android_length_to_pixel (svg_android, height_len, &height); _svg_android_length_to_pixel (svg_android, rx_len, &rx); _svg_android_length_to_pixel (svg_android, ry_len, &ry); if (rx > width / 2.0) rx = width / 2.0; if (ry > height / 2.0) ry = height / 2.0; if (rx > 0 || ry > 0) { _svg_android_move_to (svg_android, x + rx, y); _svg_android_line_to (svg_android, x + width - rx, y); _svg_android_arc_to (svg_android, rx, ry, 0, 0, 1, x + width, y + ry); _svg_android_line_to (svg_android, x + width, y + height - ry); _svg_android_arc_to (svg_android, rx, ry, 0, 0, 1, x + width - rx, y + height); _svg_android_line_to (svg_android, x + rx, y + height); _svg_android_arc_to (svg_android, rx, ry, 0, 0, 1, x, y + height - ry); _svg_android_line_to (svg_android, x, y + ry); _svg_android_arc_to (svg_android, rx, ry, 0, 0, 1, x + rx, y); } else { _svg_android_move_to (svg_android, x, y); _svg_android_line_to (svg_android, x + width, y); _svg_android_line_to (svg_android, x + width, y + height); _svg_android_line_to (svg_android, x, y + height); } _svg_android_close_path (svg_android); _svg_android_render_path (svg_android); DEBUG_EXIT("render_rect"); return SVG_ANDROID_STATUS_SUCCESS; }
svg_status_t _svg_android_set_stroke_width (void *closure, svg_length_t *width_len) { svg_android_t *svg_android = closure; double width; DEBUG_ENTRY("set_stroke_width"); _svg_android_length_to_pixel (svg_android, width_len, &width); svg_android->state->width_len.unit = width_len->unit; svg_android->state->width_len.value = width_len->value; // make sure stroke width is also scaled to fit area... ANDROID_PAINT_SET_STROKE_WIDTH(svg_android, width * svg_android->fit_to_scale); DEBUG_EXIT("set_stroke_width"); return SVG_ANDROID_STATUS_SUCCESS; }
svg_status_t _svg_android_set_gradient (svg_android_t *svg_android, svg_gradient_t *gradient, svg_android_render_type_t type) { svg_gradient_stop_t *stop; int i; jobject matrix, gradient_matrix; jobject gradient_shader = NULL; matrix = ANDROID_IDENTITY_MATRIX(svg_android); switch (gradient->units) { case SVG_GRADIENT_UNITS_USER: break; case SVG_GRADIENT_UNITS_BBOX: { jfloatArray farr; jfloat *coords; farr = ANDROID_PATH_GET_BOUNDS(svg_android, svg_android->state->path); coords = (*(svg_android->env))->GetFloatArrayElements((svg_android->env), farr, 0); // Maybe we need to add the stroke width to be correct here? (if type == SVG_ANDROID_RENDER_TYPE_STROKE) ANDROID_MATRIX_TRANSLATE(svg_android, matrix, coords[0], coords[1]); ANDROID_MATRIX_SCALE(svg_android, matrix, coords[2] - coords[0], coords[3] - coords[1]); (*(svg_android->env))->ReleaseFloatArrayElements(svg_android->env, farr, coords, 0); #if 0 // note here how the cairo version checks for fill or stroke, the extents might be different.. but for android I use the bounds of the path, this doesn't account for stroke painting outside.. double x1, y1, x2, y2; if (type == SVG_ANDROID_RENDER_TYPE_FILL) cairo_fill_extents (svg_android->cr, &x1, &y1, &x2, &y2); else cairo_stroke_extents (svg_android->cr, &x1, &y1, &x2, &y2); cairo_matrix_translate (&matrix, x1, y1); cairo_matrix_scale (&matrix, x2 - x1, y2 - y1); #endif svg_android->state->bbox = 1; } break; } // create java float array for the stops offsets, and int array for the colors jfloat offsets[gradient->num_stops]; jint colors[gradient->num_stops]; for (i = 0; i < gradient->num_stops; i++) { stop = &gradient->stops[i]; offsets[i] = stop->offset; unsigned long r, g, b, o; r = svg_color_get_red (&stop->color); g = svg_color_get_green (&stop->color); b = svg_color_get_blue (&stop->color); o = (unsigned long)(stop->opacity * 255.0); colors[i] = (o & 0xff) << 24 | (r & 0xff) << 16 | (g & 0xff) << 8 | (b & 0xff); } jfloatArray offsets_a; jintArray colors_a; offsets_a = (*(svg_android->env))->NewFloatArray(svg_android->env, gradient->num_stops); colors_a = (*(svg_android->env))->NewIntArray(svg_android->env, gradient->num_stops); (*(svg_android->env))->SetFloatArrayRegion(svg_android->env, offsets_a, 0, gradient->num_stops, offsets); (*(svg_android->env))->SetIntArrayRegion(svg_android->env, colors_a, 0, gradient->num_stops, colors); int spreadType = 2; switch (gradient->spread) { case SVG_GRADIENT_SPREAD_REPEAT: spreadType = 0; break; case SVG_GRADIENT_SPREAD_REFLECT: spreadType = 1; break; default: spreadType = 2; break; } switch (gradient->type) { case SVG_GRADIENT_LINEAR: { double x1, y1, x2, y2; _svg_android_length_to_pixel (svg_android, &gradient->u.linear.x1, &x1); _svg_android_length_to_pixel (svg_android, &gradient->u.linear.y1, &y1); _svg_android_length_to_pixel (svg_android, &gradient->u.linear.x2, &x2); _svg_android_length_to_pixel (svg_android, &gradient->u.linear.y2, &y2); if((*(svg_android->env))->ExceptionOccurred(svg_android->env)) { (*(svg_android->env))->ExceptionDescribe(svg_android->env); } gradient_shader = ANDROID_CREATE_LINEAR_GRADIENT(svg_android, x1, y1, x2, y2, colors_a, offsets_a, spreadType); if((*(svg_android->env))->ExceptionOccurred(svg_android->env)) { (*(svg_android->env))->ExceptionDescribe(svg_android->env); } } break; case SVG_GRADIENT_RADIAL: { double cx, cy, r, fx, fy; _svg_android_length_to_pixel (svg_android, &gradient->u.radial.cx, &cx); _svg_android_length_to_pixel (svg_android, &gradient->u.radial.cy, &cy); _svg_android_length_to_pixel (svg_android, &gradient->u.radial.r, &r); _svg_android_length_to_pixel (svg_android, &gradient->u.radial.fx, &fx); _svg_android_length_to_pixel (svg_android, &gradient->u.radial.fy, &fy); gradient_shader = ANDROID_CREATE_RADIAL_GRADIENT(svg_android, fx, fy, r, colors_a, offsets_a, spreadType); #if 0 // note here that there is a start and an end circel, android doesn't support that. The end circle in Android always have the same coords as the start circle pattern = cairo_pattern_create_radial (fx, fy, 0.0, cx, cy, r); #endif } break; } gradient_matrix = ANDROID_MATRIX_CREATE(svg_android, gradient->transform[0], gradient->transform[1], gradient->transform[2], gradient->transform[3], gradient->transform[4], gradient->transform[5]); ANDROID_MATRIX_MULTIPLY(svg_android, matrix, gradient_matrix); ANDROID_MATRIX_MULTIPLY(svg_android, matrix, svg_android->state->matrix); if(svg_android->fit_to_area) ANDROID_MATRIX_MULTIPLY(svg_android, matrix, svg_android->fit_to_MATRIX); if(gradient_shader) { ANDROID_SHADER_SET_MATRIX(svg_android, gradient_shader, matrix); ANDROID_PAINT_SET_SHADER(svg_android, gradient_shader); } svg_android->state->bbox = 0; return SVG_STATUS_SUCCESS; }
svg_status_t _svg_android_apply_view_box (void *closure, svg_view_box_t view_box, svg_length_t *width, svg_length_t *height) { svg_android_t *svg_android = closure; double vpar, svgar; double logic_width, logic_height; double logic_x, logic_y; double phys_width, phys_height; DEBUG_ENTRY("apply_view_box"); _svg_android_length_to_pixel (svg_android, width, &phys_width); _svg_android_length_to_pixel (svg_android, height, &phys_height); vpar = view_box.box.width / view_box.box.height; svgar = phys_width / phys_height; logic_x = view_box.box.x; logic_y = view_box.box.y; logic_width = view_box.box.width; logic_height = view_box.box.height; if (view_box.aspect_ratio == SVG_PRESERVE_ASPECT_RATIO_NONE) { ANDROID_MATRIX_SCALE(svg_android,svg_android->state->matrix, phys_width / logic_width, phys_height / logic_height); ANDROID_MATRIX_TRANSLATE(svg_android, svg_android->state->matrix, -logic_x, -logic_y); } else if ((vpar < svgar && view_box.meet_or_slice == SVG_MEET_OR_SLICE_MEET) || (vpar >= svgar && view_box.meet_or_slice == SVG_MEET_OR_SLICE_SLICE)) { ANDROID_MATRIX_SCALE(svg_android, svg_android->state->matrix, phys_height / logic_height, phys_height / logic_height); if (view_box.aspect_ratio == SVG_PRESERVE_ASPECT_RATIO_XMINYMIN || view_box.aspect_ratio == SVG_PRESERVE_ASPECT_RATIO_XMINYMID || view_box.aspect_ratio == SVG_PRESERVE_ASPECT_RATIO_XMINYMAX) ANDROID_MATRIX_TRANSLATE( svg_android, svg_android->state->matrix, -logic_x, -logic_y); else if(view_box.aspect_ratio == SVG_PRESERVE_ASPECT_RATIO_XMIDYMIN || view_box.aspect_ratio == SVG_PRESERVE_ASPECT_RATIO_XMIDYMID || view_box.aspect_ratio == SVG_PRESERVE_ASPECT_RATIO_XMIDYMAX) ANDROID_MATRIX_TRANSLATE( svg_android, svg_android->state->matrix, -logic_x - (logic_width - phys_width * logic_height / phys_height) / 2, -logic_y); else ANDROID_MATRIX_TRANSLATE( svg_android, svg_android->state->matrix, -logic_x - (logic_width - phys_width * logic_height / phys_height), -logic_y); } else { ANDROID_MATRIX_SCALE(svg_android, svg_android->state->matrix, phys_width / logic_width, phys_width / logic_width); if (view_box.aspect_ratio == SVG_PRESERVE_ASPECT_RATIO_XMINYMIN || view_box.aspect_ratio == SVG_PRESERVE_ASPECT_RATIO_XMIDYMIN || view_box.aspect_ratio == SVG_PRESERVE_ASPECT_RATIO_XMAXYMIN) ANDROID_MATRIX_TRANSLATE( svg_android, svg_android->state->matrix, -logic_x, -logic_y); else if(view_box.aspect_ratio == SVG_PRESERVE_ASPECT_RATIO_XMINYMID || view_box.aspect_ratio == SVG_PRESERVE_ASPECT_RATIO_XMIDYMID || view_box.aspect_ratio == SVG_PRESERVE_ASPECT_RATIO_XMAXYMID) ANDROID_MATRIX_TRANSLATE( svg_android, svg_android->state->matrix, -logic_x, -logic_y - (logic_height - phys_height * logic_width / phys_width) / 2); else ANDROID_MATRIX_TRANSLATE( svg_android, svg_android->state->matrix, -logic_x, -logic_y - (logic_height - phys_height * logic_width / phys_width)); } DEBUG_EXIT("apply_view_box"); return SVG_STATUS_SUCCESS; }
svg_status_t _svg_android_set_pattern (svg_android_t *svg_android, svg_element_t *pattern_element, svg_android_render_type_t type) { svg_pattern_t *pattern = svg_element_pattern (pattern_element); jobject pattern_bitmap; jobject pattern_shader; double x_px, y_px, width_px, height_px; jobject path; _svg_android_length_to_pixel (svg_android, &pattern->x, &x_px); _svg_android_length_to_pixel (svg_android, &pattern->y, &y_px); _svg_android_length_to_pixel (svg_android, &pattern->width, &width_px); _svg_android_length_to_pixel (svg_android, &pattern->height, &height_px); /* OK. We've got the final path to be filled/stroked inside the * android context right now. But we're also going to re-use that * same context to draw the pattern. And since the path is no * longer in the graphics state, android_save/restore will not help * us here. * * Currently we deal with this by manually saving/restoring the * path. * * It might be simpler to just use a new cairo_t for drawing the * pattern. */ path = svg_android->state->path; //cairo_copy_path (svg_android->cr); svg_android->state->path = ANDROID_PATH_CREATE(svg_android); // cairo_new_path (svg_android->cr); ANDROID_SAVE(svg_android); pattern_bitmap = ANDROID_CREATE_BITMAP(svg_android, (int) (width_px + 0.5), (int) (height_px + 0.5)); #if 0 pattern_surface = cairo_surface_create_similar (cairo_get_target (svg_android->cr), CAIRO_FORMAT_ARGB32, (int) (width_px + 0.5), (int) (height_px + 0.5)); #endif _svg_android_push_state (svg_android, pattern_bitmap); svg_android->state->matrix = ANDROID_IDENTITY_MATRIX(svg_android); //cairo_identity_matrix (svg_android->cr); svg_android->state->fill_paint.type = SVG_PAINT_TYPE_NONE; svg_android->state->stroke_paint.type = SVG_PAINT_TYPE_NONE; svg_element_render (pattern->group_element, &SVG_ANDROID_RENDER_ENGINE, svg_android); _svg_android_pop_state (svg_android); ANDROID_RESTORE(svg_android); svg_android->state->path = path ; #if 0 cairo_new_path (svg_android->cr); cairo_append_path (svg_android->cr, path); cairo_path_destroy (path); #endif pattern_shader = ANDROID_CREATE_BITMAP_SHADER(svg_android, pattern_bitmap); ANDROID_PAINT_SET_SHADER(svg_android, pattern_shader); #if 0 surface_pattern = cairo_pattern_create_for_surface (pattern_surface); cairo_surface_destroy (pattern_surface); cairo_pattern_set_extend (surface_pattern, CAIRO_EXTEND_REPEAT); cairo_set_source (svg_android->cr, surface_pattern); cairo_pattern_destroy (surface_pattern); #endif return SVG_STATUS_SUCCESS; }