Subversion Repositories Kolibri OS

Rev

Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed

  1. #include "fitz.h"
  2.  
  3. typedef struct fz_display_node_s fz_display_node;
  4.  
  5. #define STACK_SIZE 96
  6.  
  7. typedef enum fz_display_command_e
  8. {
  9.         FZ_CMD_FILL_PATH,
  10.         FZ_CMD_STROKE_PATH,
  11.         FZ_CMD_CLIP_PATH,
  12.         FZ_CMD_CLIP_STROKE_PATH,
  13.         FZ_CMD_FILL_TEXT,
  14.         FZ_CMD_STROKE_TEXT,
  15.         FZ_CMD_CLIP_TEXT,
  16.         FZ_CMD_CLIP_STROKE_TEXT,
  17.         FZ_CMD_IGNORE_TEXT,
  18.         FZ_CMD_FILL_SHADE,
  19.         FZ_CMD_FILL_IMAGE,
  20.         FZ_CMD_FILL_IMAGE_MASK,
  21.         FZ_CMD_CLIP_IMAGE_MASK,
  22.         FZ_CMD_POP_CLIP,
  23.         FZ_CMD_BEGIN_MASK,
  24.         FZ_CMD_END_MASK,
  25.         FZ_CMD_BEGIN_GROUP,
  26.         FZ_CMD_END_GROUP,
  27.         FZ_CMD_BEGIN_TILE,
  28.         FZ_CMD_END_TILE
  29. } fz_display_command;
  30.  
  31. struct fz_display_node_s
  32. {
  33.         fz_display_command cmd;
  34.         fz_display_node *next;
  35.         fz_rect rect;
  36.         union {
  37.                 fz_path *path;
  38.                 fz_text *text;
  39.                 fz_shade *shade;
  40.                 fz_pixmap *image;
  41.                 int blendmode;
  42.         } item;
  43.         fz_stroke_state *stroke;
  44.         int flag; /* even_odd, accumulate, isolated/knockout... */
  45.         fz_matrix ctm;
  46.         fz_colorspace *colorspace;
  47.         float alpha;
  48.         float color[FZ_MAX_COLORS];
  49. };
  50.  
  51. struct fz_display_list_s
  52. {
  53.         fz_display_node *first;
  54.         fz_display_node *last;
  55.  
  56.         int top;
  57.         struct {
  58.                 fz_rect *update;
  59.                 fz_rect rect;
  60.         } stack[STACK_SIZE];
  61.         int tiled;
  62. };
  63.  
  64. enum { ISOLATED = 1, KNOCKOUT = 2 };
  65.  
  66. static fz_display_node *
  67. fz_new_display_node(fz_display_command cmd, fz_matrix ctm,
  68.         fz_colorspace *colorspace, float *color, float alpha)
  69. {
  70.         fz_display_node *node;
  71.         int i;
  72.  
  73.         node = fz_malloc(sizeof(fz_display_node));
  74.         node->cmd = cmd;
  75.         node->next = NULL;
  76.         node->rect = fz_empty_rect;
  77.         node->item.path = NULL;
  78.         node->stroke = NULL;
  79.         node->flag = 0;
  80.         node->ctm = ctm;
  81.         if (colorspace)
  82.         {
  83.                 node->colorspace = fz_keep_colorspace(colorspace);
  84.                 if (color)
  85.                 {
  86.                         for (i = 0; i < node->colorspace->n; i++)
  87.                                 node->color[i] = color[i];
  88.                 }
  89.         }
  90.         else
  91.         {
  92.                 node->colorspace = NULL;
  93.         }
  94.         node->alpha = alpha;
  95.  
  96.         return node;
  97. }
  98.  
  99. static fz_stroke_state *
  100. fz_clone_stroke_state(fz_stroke_state *stroke)
  101. {
  102.         fz_stroke_state *newstroke = fz_malloc(sizeof(fz_stroke_state));
  103.         *newstroke = *stroke;
  104.         return newstroke;
  105. }
  106.  
  107. static void
  108. fz_append_display_node(fz_display_list *list, fz_display_node *node)
  109. {
  110.         switch (node->cmd)
  111.         {
  112.         case FZ_CMD_CLIP_PATH:
  113.         case FZ_CMD_CLIP_STROKE_PATH:
  114.         case FZ_CMD_CLIP_IMAGE_MASK:
  115.                 if (list->top < STACK_SIZE)
  116.                 {
  117.                         list->stack[list->top].update = &node->rect;
  118.                         list->stack[list->top].rect = fz_empty_rect;
  119.                 }
  120.                 list->top++;
  121.                 break;
  122.         case FZ_CMD_END_MASK:
  123.         case FZ_CMD_CLIP_TEXT:
  124.         case FZ_CMD_CLIP_STROKE_TEXT:
  125.                 if (list->top < STACK_SIZE)
  126.                 {
  127.                         list->stack[list->top].update = NULL;
  128.                         list->stack[list->top].rect = fz_empty_rect;
  129.                 }
  130.                 list->top++;
  131.                 break;
  132.         case FZ_CMD_BEGIN_TILE:
  133.                 list->tiled++;
  134.                 if (list->top > 0 && list->top < STACK_SIZE)
  135.                 {
  136.                         list->stack[list->top-1].rect = fz_infinite_rect;
  137.                 }
  138.                 break;
  139.         case FZ_CMD_END_TILE:
  140.                 list->tiled--;
  141.                 break;
  142.         case FZ_CMD_END_GROUP:
  143.                 break;
  144.         case FZ_CMD_POP_CLIP:
  145.                 if (list->top > STACK_SIZE)
  146.                 {
  147.                         list->top--;
  148.                         node->rect = fz_infinite_rect;
  149.                 }
  150.                 else if (list->top > 0)
  151.                 {
  152.                         fz_rect *update;
  153.                         list->top--;
  154.                         update = list->stack[list->top].update;
  155.                         if (list->tiled == 0)
  156.                         {
  157.                                 if (update != NULL)
  158.                                 {
  159.                                         *update = fz_intersect_rect(*update, list->stack[list->top].rect);
  160.                                         node->rect = *update;
  161.                                 }
  162.                                 else
  163.                                         node->rect = list->stack[list->top].rect;
  164.                         }
  165.                         else
  166.                                 node->rect = fz_infinite_rect;
  167.                 }
  168.                 /* fallthrough */
  169.         default:
  170.                 if (list->top > 0 && list->tiled == 0 && list->top <= STACK_SIZE)
  171.                         list->stack[list->top-1].rect = fz_union_rect(list->stack[list->top-1].rect, node->rect);
  172.                 break;
  173.         }
  174.         if (!list->first)
  175.         {
  176.                 list->first = node;
  177.                 list->last = node;
  178.         }
  179.         else
  180.         {
  181.                 list->last->next = node;
  182.                 list->last = node;
  183.         }
  184. }
  185.  
  186. static void
  187. fz_free_display_node(fz_display_node *node)
  188. {
  189.         switch (node->cmd)
  190.         {
  191.         case FZ_CMD_FILL_PATH:
  192.         case FZ_CMD_STROKE_PATH:
  193.         case FZ_CMD_CLIP_PATH:
  194.         case FZ_CMD_CLIP_STROKE_PATH:
  195.                 fz_free_path(node->item.path);
  196.                 break;
  197.         case FZ_CMD_FILL_TEXT:
  198.         case FZ_CMD_STROKE_TEXT:
  199.         case FZ_CMD_CLIP_TEXT:
  200.         case FZ_CMD_CLIP_STROKE_TEXT:
  201.         case FZ_CMD_IGNORE_TEXT:
  202.                 fz_free_text(node->item.text);
  203.                 break;
  204.         case FZ_CMD_FILL_SHADE:
  205.                 fz_drop_shade(node->item.shade);
  206.                 break;
  207.         case FZ_CMD_FILL_IMAGE:
  208.         case FZ_CMD_FILL_IMAGE_MASK:
  209.         case FZ_CMD_CLIP_IMAGE_MASK:
  210.                 fz_drop_pixmap(node->item.image);
  211.                 break;
  212.         case FZ_CMD_POP_CLIP:
  213.         case FZ_CMD_BEGIN_MASK:
  214.         case FZ_CMD_END_MASK:
  215.         case FZ_CMD_BEGIN_GROUP:
  216.         case FZ_CMD_END_GROUP:
  217.         case FZ_CMD_BEGIN_TILE:
  218.         case FZ_CMD_END_TILE:
  219.                 break;
  220.         }
  221.         if (node->stroke)
  222.                 fz_free(node->stroke);
  223.         if (node->colorspace)
  224.                 fz_drop_colorspace(node->colorspace);
  225.         fz_free(node);
  226. }
  227.  
  228. static void
  229. fz_list_fill_path(void *user, fz_path *path, int even_odd, fz_matrix ctm,
  230.         fz_colorspace *colorspace, float *color, float alpha)
  231. {
  232.         fz_display_node *node;
  233.         node = fz_new_display_node(FZ_CMD_FILL_PATH, ctm, colorspace, color, alpha);
  234.         node->rect = fz_bound_path(path, NULL, ctm);
  235.         node->item.path = fz_clone_path(path);
  236.         node->flag = even_odd;
  237.         fz_append_display_node(user, node);
  238. }
  239.  
  240. static void
  241. fz_list_stroke_path(void *user, fz_path *path, fz_stroke_state *stroke, fz_matrix ctm,
  242.         fz_colorspace *colorspace, float *color, float alpha)
  243. {
  244.         fz_display_node *node;
  245.         node = fz_new_display_node(FZ_CMD_STROKE_PATH, ctm, colorspace, color, alpha);
  246.         node->rect = fz_bound_path(path, stroke, ctm);
  247.         node->item.path = fz_clone_path(path);
  248.         node->stroke = fz_clone_stroke_state(stroke);
  249.         fz_append_display_node(user, node);
  250. }
  251.  
  252. static void
  253. fz_list_clip_path(void *user, fz_path *path, fz_rect *rect, int even_odd, fz_matrix ctm)
  254. {
  255.         fz_display_node *node;
  256.         node = fz_new_display_node(FZ_CMD_CLIP_PATH, ctm, NULL, NULL, 0);
  257.         node->rect = fz_bound_path(path, NULL, ctm);
  258.         if (rect != NULL)
  259.                 node->rect = fz_intersect_rect(node->rect, *rect);
  260.         node->item.path = fz_clone_path(path);
  261.         node->flag = even_odd;
  262.         fz_append_display_node(user, node);
  263. }
  264.  
  265. static void
  266. fz_list_clip_stroke_path(void *user, fz_path *path, fz_rect *rect, fz_stroke_state *stroke, fz_matrix ctm)
  267. {
  268.         fz_display_node *node;
  269.         node = fz_new_display_node(FZ_CMD_CLIP_STROKE_PATH, ctm, NULL, NULL, 0);
  270.         node->rect = fz_bound_path(path, stroke, ctm);
  271.         if (rect != NULL)
  272.                 node->rect = fz_intersect_rect(node->rect, *rect);
  273.         node->item.path = fz_clone_path(path);
  274.         node->stroke = fz_clone_stroke_state(stroke);
  275.         fz_append_display_node(user, node);
  276. }
  277.  
  278. static void
  279. fz_list_fill_text(void *user, fz_text *text, fz_matrix ctm,
  280.         fz_colorspace *colorspace, float *color, float alpha)
  281. {
  282.         fz_display_node *node;
  283.         node = fz_new_display_node(FZ_CMD_FILL_TEXT, ctm, colorspace, color, alpha);
  284.         node->rect = fz_bound_text(text, ctm);
  285.         node->item.text = fz_clone_text(text);
  286.         fz_append_display_node(user, node);
  287. }
  288.  
  289. static void
  290. fz_list_stroke_text(void *user, fz_text *text, fz_stroke_state *stroke, fz_matrix ctm,
  291.         fz_colorspace *colorspace, float *color, float alpha)
  292. {
  293.         fz_display_node *node;
  294.         node = fz_new_display_node(FZ_CMD_STROKE_TEXT, ctm, colorspace, color, alpha);
  295.         node->rect = fz_bound_text(text, ctm);
  296.         node->item.text = fz_clone_text(text);
  297.         node->stroke = fz_clone_stroke_state(stroke);
  298.         fz_append_display_node(user, node);
  299. }
  300.  
  301. static void
  302. fz_list_clip_text(void *user, fz_text *text, fz_matrix ctm, int accumulate)
  303. {
  304.         fz_display_node *node;
  305.         node = fz_new_display_node(FZ_CMD_CLIP_TEXT, ctm, NULL, NULL, 0);
  306.         node->rect = fz_bound_text(text, ctm);
  307.         node->item.text = fz_clone_text(text);
  308.         node->flag = accumulate;
  309.         /* when accumulating, be conservative about culling */
  310.         if (accumulate)
  311.                 node->rect = fz_infinite_rect;
  312.         fz_append_display_node(user, node);
  313. }
  314.  
  315. static void
  316. fz_list_clip_stroke_text(void *user, fz_text *text, fz_stroke_state *stroke, fz_matrix ctm)
  317. {
  318.         fz_display_node *node;
  319.         node = fz_new_display_node(FZ_CMD_CLIP_STROKE_TEXT, ctm, NULL, NULL, 0);
  320.         node->rect = fz_bound_text(text, ctm);
  321.         node->item.text = fz_clone_text(text);
  322.         node->stroke = fz_clone_stroke_state(stroke);
  323.         fz_append_display_node(user, node);
  324. }
  325.  
  326. static void
  327. fz_list_ignore_text(void *user, fz_text *text, fz_matrix ctm)
  328. {
  329.         fz_display_node *node;
  330.         node = fz_new_display_node(FZ_CMD_IGNORE_TEXT, ctm, NULL, NULL, 0);
  331.         node->rect = fz_bound_text(text, ctm);
  332.         node->item.text = fz_clone_text(text);
  333.         fz_append_display_node(user, node);
  334. }
  335.  
  336. static void
  337. fz_list_pop_clip(void *user)
  338. {
  339.         fz_display_node *node;
  340.         node = fz_new_display_node(FZ_CMD_POP_CLIP, fz_identity, NULL, NULL, 0);
  341.         fz_append_display_node(user, node);
  342. }
  343.  
  344. static void
  345. fz_list_fill_shade(void *user, fz_shade *shade, fz_matrix ctm, float alpha)
  346. {
  347.         fz_display_node *node;
  348.         node = fz_new_display_node(FZ_CMD_FILL_SHADE, ctm, NULL, NULL, alpha);
  349.         node->rect = fz_bound_shade(shade, ctm);
  350.         node->item.shade = fz_keep_shade(shade);
  351.         fz_append_display_node(user, node);
  352. }
  353.  
  354. static void
  355. fz_list_fill_image(void *user, fz_pixmap *image, fz_matrix ctm, float alpha)
  356. {
  357.         fz_display_node *node;
  358.         node = fz_new_display_node(FZ_CMD_FILL_IMAGE, ctm, NULL, NULL, alpha);
  359.         node->rect = fz_transform_rect(ctm, fz_unit_rect);
  360.         node->item.image = fz_keep_pixmap(image);
  361.         fz_append_display_node(user, node);
  362. }
  363.  
  364. static void
  365. fz_list_fill_image_mask(void *user, fz_pixmap *image, fz_matrix ctm,
  366.         fz_colorspace *colorspace, float *color, float alpha)
  367. {
  368.         fz_display_node *node;
  369.         node = fz_new_display_node(FZ_CMD_FILL_IMAGE_MASK, ctm, colorspace, color, alpha);
  370.         node->rect = fz_transform_rect(ctm, fz_unit_rect);
  371.         node->item.image = fz_keep_pixmap(image);
  372.         fz_append_display_node(user, node);
  373. }
  374.  
  375. static void
  376. fz_list_clip_image_mask(void *user, fz_pixmap *image, fz_rect *rect, fz_matrix ctm)
  377. {
  378.         fz_display_node *node;
  379.         node = fz_new_display_node(FZ_CMD_CLIP_IMAGE_MASK, ctm, NULL, NULL, 0);
  380.         node->rect = fz_transform_rect(ctm, fz_unit_rect);
  381.         if (rect != NULL)
  382.                 node->rect = fz_intersect_rect(node->rect, *rect);
  383.         node->item.image = fz_keep_pixmap(image);
  384.         fz_append_display_node(user, node);
  385. }
  386.  
  387. static void
  388. fz_list_begin_mask(void *user, fz_rect rect, int luminosity, fz_colorspace *colorspace, float *color)
  389. {
  390.         fz_display_node *node;
  391.         node = fz_new_display_node(FZ_CMD_BEGIN_MASK, fz_identity, colorspace, color, 0);
  392.         node->rect = rect;
  393.         node->flag = luminosity;
  394.         fz_append_display_node(user, node);
  395. }
  396.  
  397. static void
  398. fz_list_end_mask(void *user)
  399. {
  400.         fz_display_node *node;
  401.         node = fz_new_display_node(FZ_CMD_END_MASK, fz_identity, NULL, NULL, 0);
  402.         fz_append_display_node(user, node);
  403. }
  404.  
  405. static void
  406. fz_list_begin_group(void *user, fz_rect rect, int isolated, int knockout, int blendmode, float alpha)
  407. {
  408.         fz_display_node *node;
  409.         node = fz_new_display_node(FZ_CMD_BEGIN_GROUP, fz_identity, NULL, NULL, alpha);
  410.         node->rect = rect;
  411.         node->item.blendmode = blendmode;
  412.         node->flag |= isolated ? ISOLATED : 0;
  413.         node->flag |= knockout ? KNOCKOUT : 0;
  414.         fz_append_display_node(user, node);
  415. }
  416.  
  417. static void
  418. fz_list_end_group(void *user)
  419. {
  420.         fz_display_node *node;
  421.         node = fz_new_display_node(FZ_CMD_END_GROUP, fz_identity, NULL, NULL, 0);
  422.         fz_append_display_node(user, node);
  423. }
  424.  
  425. static void
  426. fz_list_begin_tile(void *user, fz_rect area, fz_rect view, float xstep, float ystep, fz_matrix ctm)
  427. {
  428.         fz_display_node *node;
  429.         node = fz_new_display_node(FZ_CMD_BEGIN_TILE, ctm, NULL, NULL, 0);
  430.         node->rect = area;
  431.         node->color[0] = xstep;
  432.         node->color[1] = ystep;
  433.         node->color[2] = view.x0;
  434.         node->color[3] = view.y0;
  435.         node->color[4] = view.x1;
  436.         node->color[5] = view.y1;
  437.         fz_append_display_node(user, node);
  438. }
  439.  
  440. static void
  441. fz_list_end_tile(void *user)
  442. {
  443.         fz_display_node *node;
  444.         node = fz_new_display_node(FZ_CMD_END_TILE, fz_identity, NULL, NULL, 0);
  445.         fz_append_display_node(user, node);
  446. }
  447.  
  448. fz_device *
  449. fz_new_list_device(fz_display_list *list)
  450. {
  451.         fz_device *dev = fz_new_device(list);
  452.  
  453.         dev->fill_path = fz_list_fill_path;
  454.         dev->stroke_path = fz_list_stroke_path;
  455.         dev->clip_path = fz_list_clip_path;
  456.         dev->clip_stroke_path = fz_list_clip_stroke_path;
  457.  
  458.         dev->fill_text = fz_list_fill_text;
  459.         dev->stroke_text = fz_list_stroke_text;
  460.         dev->clip_text = fz_list_clip_text;
  461.         dev->clip_stroke_text = fz_list_clip_stroke_text;
  462.         dev->ignore_text = fz_list_ignore_text;
  463.  
  464.         dev->fill_shade = fz_list_fill_shade;
  465.         dev->fill_image = fz_list_fill_image;
  466.         dev->fill_image_mask = fz_list_fill_image_mask;
  467.         dev->clip_image_mask = fz_list_clip_image_mask;
  468.  
  469.         dev->pop_clip = fz_list_pop_clip;
  470.  
  471.         dev->begin_mask = fz_list_begin_mask;
  472.         dev->end_mask = fz_list_end_mask;
  473.         dev->begin_group = fz_list_begin_group;
  474.         dev->end_group = fz_list_end_group;
  475.  
  476.         dev->begin_tile = fz_list_begin_tile;
  477.         dev->end_tile = fz_list_end_tile;
  478.  
  479.         return dev;
  480. }
  481.  
  482. fz_display_list *
  483. fz_new_display_list(void)
  484. {
  485.         fz_display_list *list = fz_malloc(sizeof(fz_display_list));
  486.         list->first = NULL;
  487.         list->last = NULL;
  488.         list->top = 0;
  489.         list->tiled = 0;
  490.         return list;
  491. }
  492.  
  493. void
  494. fz_free_display_list(fz_display_list *list)
  495. {
  496.         fz_display_node *node = list->first;
  497.         while (node)
  498.         {
  499.                 fz_display_node *next = node->next;
  500.                 fz_free_display_node(node);
  501.                 node = next;
  502.         }
  503.         fz_free(list);
  504. }
  505.  
  506. void
  507. fz_execute_display_list(fz_display_list *list, fz_device *dev, fz_matrix top_ctm, fz_bbox scissor)
  508. {
  509.         fz_display_node *node;
  510.         fz_matrix ctm;
  511.         fz_rect rect;
  512.         fz_bbox bbox;
  513.         int clipped = 0;
  514.         int tiled = 0;
  515.         int empty;
  516.  
  517.         if (!fz_is_infinite_bbox(scissor))
  518.         {
  519.                 /* add some fuzz at the edges, as especially glyph rects
  520.                  * are sometimes not actually completely bounding the glyph */
  521.                 scissor.x0 -= 20; scissor.y0 -= 20;
  522.                 scissor.x1 += 20; scissor.y1 += 20;
  523.         }
  524.  
  525.         for (node = list->first; node; node = node->next)
  526.         {
  527.                 /* cull objects to draw using a quick visibility test */
  528.  
  529.                 if (tiled || node->cmd == FZ_CMD_BEGIN_TILE || node->cmd == FZ_CMD_END_TILE)
  530.                 {
  531.                         empty = 0;
  532.                 }
  533.                 else
  534.                 {
  535.                         bbox = fz_round_rect(fz_transform_rect(top_ctm, node->rect));
  536.                         bbox = fz_intersect_bbox(bbox, scissor);
  537.                         empty = fz_is_empty_bbox(bbox);
  538.                 }
  539.  
  540.                 if (clipped || empty)
  541.                 {
  542.                         switch (node->cmd)
  543.                         {
  544.                         case FZ_CMD_CLIP_PATH:
  545.                         case FZ_CMD_CLIP_STROKE_PATH:
  546.                         case FZ_CMD_CLIP_TEXT:
  547.                         case FZ_CMD_CLIP_STROKE_TEXT:
  548.                         case FZ_CMD_CLIP_IMAGE_MASK:
  549.                         case FZ_CMD_BEGIN_MASK:
  550.                         case FZ_CMD_BEGIN_GROUP:
  551.                                 clipped++;
  552.                                 continue;
  553.                         case FZ_CMD_POP_CLIP:
  554.                         case FZ_CMD_END_GROUP:
  555.                                 if (!clipped)
  556.                                         goto visible;
  557.                                 clipped--;
  558.                                 continue;
  559.                         case FZ_CMD_END_MASK:
  560.                                 if (!clipped)
  561.                                         goto visible;
  562.                                 continue;
  563.                         default:
  564.                                 continue;
  565.                         }
  566.                 }
  567.  
  568. visible:
  569.                 ctm = fz_concat(node->ctm, top_ctm);
  570.  
  571.                 switch (node->cmd)
  572.                 {
  573.                 case FZ_CMD_FILL_PATH:
  574.                         fz_fill_path(dev, node->item.path, node->flag, ctm,
  575.                                 node->colorspace, node->color, node->alpha);
  576.                         break;
  577.                 case FZ_CMD_STROKE_PATH:
  578.                         fz_stroke_path(dev, node->item.path, node->stroke, ctm,
  579.                                 node->colorspace, node->color, node->alpha);
  580.                         break;
  581.                 case FZ_CMD_CLIP_PATH:
  582.                 {
  583.                         fz_rect trect = fz_transform_rect(top_ctm, node->rect);
  584.                         fz_clip_path(dev, node->item.path, &trect, node->flag, ctm);
  585.                         break;
  586.                 }
  587.                 case FZ_CMD_CLIP_STROKE_PATH:
  588.                 {
  589.                         fz_rect trect = fz_transform_rect(top_ctm, node->rect);
  590.                         fz_clip_stroke_path(dev, node->item.path, &trect, node->stroke, ctm);
  591.                         break;
  592.                 }
  593.                 case FZ_CMD_FILL_TEXT:
  594.                         fz_fill_text(dev, node->item.text, ctm,
  595.                                 node->colorspace, node->color, node->alpha);
  596.                         break;
  597.                 case FZ_CMD_STROKE_TEXT:
  598.                         fz_stroke_text(dev, node->item.text, node->stroke, ctm,
  599.                                 node->colorspace, node->color, node->alpha);
  600.                         break;
  601.                 case FZ_CMD_CLIP_TEXT:
  602.                         fz_clip_text(dev, node->item.text, ctm, node->flag);
  603.                         break;
  604.                 case FZ_CMD_CLIP_STROKE_TEXT:
  605.                         fz_clip_stroke_text(dev, node->item.text, node->stroke, ctm);
  606.                         break;
  607.                 case FZ_CMD_IGNORE_TEXT:
  608.                         fz_ignore_text(dev, node->item.text, ctm);
  609.                         break;
  610.                 case FZ_CMD_FILL_SHADE:
  611.                         fz_fill_shade(dev, node->item.shade, ctm, node->alpha);
  612.                         break;
  613.                 case FZ_CMD_FILL_IMAGE:
  614.                         fz_fill_image(dev, node->item.image, ctm, node->alpha);
  615.                         break;
  616.                 case FZ_CMD_FILL_IMAGE_MASK:
  617.                         fz_fill_image_mask(dev, node->item.image, ctm,
  618.                                 node->colorspace, node->color, node->alpha);
  619.                         break;
  620.                 case FZ_CMD_CLIP_IMAGE_MASK:
  621.                 {
  622.                         fz_rect trect = fz_transform_rect(top_ctm, node->rect);
  623.                         fz_clip_image_mask(dev, node->item.image, &trect, ctm);
  624.                         break;
  625.                 }
  626.                 case FZ_CMD_POP_CLIP:
  627.                         fz_pop_clip(dev);
  628.                         break;
  629.                 case FZ_CMD_BEGIN_MASK:
  630.                         rect = fz_transform_rect(top_ctm, node->rect);
  631.                         fz_begin_mask(dev, rect, node->flag, node->colorspace, node->color);
  632.                         break;
  633.                 case FZ_CMD_END_MASK:
  634.                         fz_end_mask(dev);
  635.                         break;
  636.                 case FZ_CMD_BEGIN_GROUP:
  637.                         rect = fz_transform_rect(top_ctm, node->rect);
  638.                         fz_begin_group(dev, rect,
  639.                                 (node->flag & ISOLATED) != 0, (node->flag & KNOCKOUT) != 0,
  640.                                 node->item.blendmode, node->alpha);
  641.                         break;
  642.                 case FZ_CMD_END_GROUP:
  643.                         fz_end_group(dev);
  644.                         break;
  645.                 case FZ_CMD_BEGIN_TILE:
  646.                         tiled++;
  647.                         rect.x0 = node->color[2];
  648.                         rect.y0 = node->color[3];
  649.                         rect.x1 = node->color[4];
  650.                         rect.y1 = node->color[5];
  651.                         fz_begin_tile(dev, node->rect, rect,
  652.                                 node->color[0], node->color[1], ctm);
  653.                         break;
  654.                 case FZ_CMD_END_TILE:
  655.                         tiled--;
  656.                         fz_end_tile(dev);
  657.                         break;
  658.                 }
  659.         }
  660. }
  661.