From 7ee19483ed81a885f464d4e93f4eefb3d4037d30 Mon Sep 17 00:00:00 2001 From: Tor Andersson Date: Sat, 4 Jun 2005 15:58:45 +0200 Subject: new world order --- world/node_optimize.c | 313 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 313 insertions(+) create mode 100644 world/node_optimize.c (limited to 'world/node_optimize.c') diff --git a/world/node_optimize.c b/world/node_optimize.c new file mode 100644 index 00000000..80a43293 --- /dev/null +++ b/world/node_optimize.c @@ -0,0 +1,313 @@ +#include + +/* + * Remove (mask ... white) until we get something not white + */ + +static int iswhitenode(fz_colornode *node) +{ + if (!strcmp(node->cs->name, "DeviceGray")) + return fabs(node->samples[0] - 1.0) < FLT_EPSILON; + if (!strcmp(node->cs->name, "DeviceRGB")) + return fabs(node->samples[0] - 1.0) < FLT_EPSILON && + fabs(node->samples[1] - 1.0) < FLT_EPSILON && + fabs(node->samples[2] - 1.0) < FLT_EPSILON; + if (!strcmp(node->cs->name, "DeviceCMYK")) + return fabs(node->samples[0]) < FLT_EPSILON && + fabs(node->samples[1]) < FLT_EPSILON && + fabs(node->samples[2]) < FLT_EPSILON && + fabs(node->samples[3]) < FLT_EPSILON; + return 0; +} + +static int cleanwhite(fz_node *node) +{ + fz_node *current; + fz_node *next; + fz_node *shape; + fz_node *color; + + for (current = node->first; current; current = next) + { + next = current->next; + + if (fz_islinknode(current)) + return 1; + else if (fz_isimagenode(current)) + return 1; + else if (fz_isshadenode(current)) + return 1; + else if (fz_iscolornode(current)) + { + if (!iswhitenode((fz_colornode*)current)) + return 1; + } + + else if (fz_ismasknode(current)) + { + shape = current->first; + color = shape->next; + if (fz_iscolornode(color)) + { + if (iswhitenode((fz_colornode*)color)) + fz_removenode(current); + else + return 1; + } + else + { + if (cleanwhite(current)) + return 1; + } + } + + else + { + if (cleanwhite(current)) + return 1; + } + } + + return 0; +} + +/* + * Remove useless overs that only have one child. + */ + +static void cleanovers(fz_node *node) +{ + fz_node *prev; + fz_node *next; + fz_node *current; + fz_node *child; + + prev = nil; + for (current = node->first; current; current = next) + { + next = current->next; + + if (fz_isovernode(current)) + { + if (current->first == current->last) + { + child = current->first; + fz_removenode(current); + if (child) + { + if (prev) + fz_insertnodeafter(prev, child); + else + fz_insertnodefirst(node, child); + } + current = child; + } + } + + if (current) + prev = current; + } + + for (current = node->first; current; current = current->next) + cleanovers(current); +} + +/* + * Remove rectangular clip-masks whose contents fit... + */ + +static int getrect(fz_pathnode *path, fz_rect *bboxp) +{ + float x, y, w, h; + + /* move x y, line x+w y, line x+w y+h, line x y+h, close */ + + if (path->len != 13) + return 0; + + if (path->els[0].k != FZ_MOVETO) return 0; + x = path->els[1].v; + y = path->els[2].v; + + if (path->els[3].k != FZ_LINETO) return 0; + w = path->els[4].v - x; + if (path->els[5].v != y) return 0; + + if (path->els[6].k != FZ_LINETO) return 0; + if (path->els[7].v != x + w) return 0; + h = path->els[8].v - y; + + if (path->els[9].k != FZ_LINETO) return 0; + if (path->els[10].v != x) return 0; + if (path->els[11].v != y + h) return 0; + + if (path->els[12].k != FZ_CLOSEPATH) return 0; + + bboxp->x0 = MIN(x, x + w); + bboxp->y0 = MIN(y, y + h); + bboxp->x1 = MAX(x, x + w); + bboxp->y1 = MAX(y, y + h); + + return 1; +} + +static int fitsinside(fz_node *node, fz_rect clip) +{ + fz_rect bbox; + bbox = fz_boundnode(node, fz_identity()); + if (fz_isinfiniterect(bbox)) return 0; + if (fz_isemptyrect(bbox)) return 1; + if (bbox.x0 < clip.x0) return 0; + if (bbox.x1 > clip.x1) return 0; + if (bbox.y0 < clip.y0) return 0; + if (bbox.y1 > clip.y1) return 0; + return 1; +} + +static void cleanmasks(fz_node *node) +{ + fz_node *prev; + fz_node *current; + fz_node *shape; + fz_node *color; + fz_rect bbox; + + for (current = node->first; current; current = current->next) + cleanmasks(current); + + prev = nil; + for (current = node->first; current; current = current->next) + { +retry: + if (!current) + break; + + if (fz_ismasknode(current)) + { + shape = current->first; + color = shape->next; + + if (color == nil) + { + fz_removenode(current); + prev = nil; + current = node->first; + goto retry; + } + + if (fz_ispathnode(shape)) + { + if (getrect((fz_pathnode*)shape, &bbox)) + { + if (fitsinside(color, bbox)) + { + fz_removenode(current); + if (prev) + fz_insertnodeafter(prev, color); + else + fz_insertnodefirst(node, color); + current = color; + goto retry; + } + } + } + } + + prev = current; + } +} + +/* + * Turn 1x1 images into rectangle fills + */ + +static fz_error *clean1x1(fz_node *node) +{ + fz_error *error; + fz_node *current; + fz_node *color; + fz_pathnode *rect; + fz_node *mask; + fz_image *image; + fz_pixmap *pix; + float v[FZ_MAXCOLORS]; + int i; + + for (current = node->first; current; current = current->next) + { + if (fz_isimagenode(current)) + { + image = ((fz_imagenode*)current)->image; + if (image->w == 1 && image->h == 1) + { + error = fz_newpathnode(&rect); + fz_moveto(rect, 0, 0); + fz_lineto(rect, 1, 0); + fz_lineto(rect, 1, 1); + fz_lineto(rect, 0, 1); + fz_closepath(rect); + fz_endpath(rect, FZ_FILL, nil, nil); + + if (image->cs) + { + error = fz_newpixmap(&pix, 0, 0, 1, 1, image->n + 1); + if (error) + return error; + + error = image->loadtile(image, pix); + if (error) + return error; + + for (i = 0; i < image->n; i++) + v[i] = pix->samples[i + 1] / 255.0; + + fz_droppixmap(pix); + + error = fz_newcolornode(&color, image->cs, image->n, v); + if (error) + return error; + error = fz_newmasknode(&mask); + if (error) + return error; + + fz_insertnodeafter(current, mask); + fz_insertnodelast(mask, (fz_node*)rect); + fz_insertnodelast(mask, color); + fz_removenode(current); + current = mask; + } + + else + { + /* pray that the 1x1 image mask is all opaque */ + fz_insertnodeafter(current, (fz_node*)rect); + fz_removenode(current); + current = (fz_node*)rect; + } + } + } + + error = clean1x1(current); + if (error) + return error; + } + + return nil; +} + +/* + * + */ + +fz_error * +fz_optimizetree(fz_tree *tree) +{ + if (getenv("DONTOPT")) + return nil; + cleanwhite(tree->root); + cleanovers(tree->root); + cleanmasks(tree->root); + clean1x1(tree->root); + return nil; +} + -- cgit v1.2.3