#include "fitz_base.h" #include "fitz_tree.h" /* * Remove (mask ... white) until we get something not white */ static int iswhitenode(fz_solidnode *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_issolidnode(current)) { if (!iswhitenode((fz_solidnode*)current)) return 1; } else if (fz_ismasknode(current)) { shape = current->first; color = shape->next; if (fz_issolidnode(color)) { if (iswhitenode((fz_solidnode*)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; } } /* * */ fz_error * fz_optimizetree(fz_tree *tree) { if (getenv("DONTOPT")) return fz_okay; cleanwhite(tree->root); cleanovers(tree->root); cleanmasks(tree->root); return fz_okay; }