added basic support for split up background bitmaps
[swftools.git] / lib / pdf / BitmapOutputDev.cc
1 /* InfoOutputDev.h
2
3    Output Device which creates a bitmap.
4
5    Swftools is free software; you can redistribute it and/or modify
6    it under the terms of the GNU General Public License as published by
7    the Free Software Foundation; either version 2 of the License, or
8    (at your option) any later version.
9
10    Swftools is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13    GNU General Public License for more details.
14
15    You should have received a copy of the GNU General Public License
16    along with swftools; if not, write to the Free Software
17    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */
18
19 #include <stdlib.h>
20 #include <stdio.h>
21 #include <memory.h>
22 #include <assert.h>
23 #include "config.h"
24 #include "BitmapOutputDev.h"
25 #include "GFXOutputDev.h"
26 #include "SplashBitmap.h"
27 #include "SplashPattern.h"
28 #include "Splash.h"
29 #include "../log.h"
30 #include "../png.h"
31 #include "../devices/record.h"
32 #include "../types.h"
33
34 #define UNKNOWN_BOUNDING_BOX 0,0,0,0
35
36 static SplashColor splash_white = {255,255,255};
37 static SplashColor splash_black = {0,0,0};
38     
39 ClipState::ClipState()
40 {
41     this->next = 0;
42     this->clipbitmap = 0;
43     this->written = 0;
44 }
45
46 BitmapOutputDev::BitmapOutputDev(InfoOutputDev*info, PDFDoc*doc)
47 {
48     this->info = info;
49     this->doc = doc;
50     this->xref = doc->getXRef();
51     
52     /* color graphic output device, for creating bitmaps */
53     this->rgbdev = new SplashOutputDev(splashModeRGB8, 1, gFalse, splash_white, gTrue, gTrue);
54   
55     /* color mode for binary bitmaps */
56     SplashColorMode colorMode = splashModeMono1;
57
58     /* two devices for testing things against clipping: one clips, the other doesn't */
59     this->clip0dev = new SplashOutputDev(colorMode, 1, gFalse, splash_black, gTrue, gFalse);
60     this->clip1dev = new SplashOutputDev(colorMode, 1, gFalse, splash_black, gTrue, gFalse);
61     
62     /* device indicating where polygonal pixels were drawn */
63     this->boolpolydev = new SplashOutputDev(colorMode, 1, gFalse, splash_black, gTrue, gFalse);
64     /* device indicating where text pixels were drawn */
65     this->booltextdev = new SplashOutputDev(colorMode, 1, gFalse, splash_black, gTrue, gFalse);
66
67     /* device for handling texts and links */
68     this->gfxdev = new GFXOutputDev(info, this->doc);
69
70     this->rgbdev->startDoc(this->xref);
71     this->boolpolydev->startDoc(this->xref);
72     this->booltextdev->startDoc(this->xref);
73     this->clip0dev->startDoc(this->xref);
74     this->clip1dev->startDoc(this->xref);
75
76     this->gfxoutput = (gfxdevice_t*)malloc(sizeof(gfxdevice_t));
77     gfxdevice_record_init(this->gfxoutput);
78
79     this->gfxdev->setDevice(this->gfxoutput);
80     
81     this->config_extrafontdata = 0;
82     this->bboxpath = 0;
83     //this->clipdev = 0;
84     //this->clipstates = 0;
85 }
86 BitmapOutputDev::~BitmapOutputDev()
87 {
88     if(this->gfxoutput) {
89         gfxresult_t*r = this->gfxoutput->finish(this->gfxoutput);
90         r->destroy(r);
91         free(this->gfxoutput);this->gfxoutput = 0;
92     }
93     if(this->bboxpath) {
94         delete this->bboxpath;this->bboxpath = 0;
95     }
96     if(this->rgbdev) {
97         delete this->rgbdev;this->rgbdev = 0;
98     }
99     if(this->gfxdev) {
100         delete this->gfxdev;this->gfxdev= 0;
101     }
102     if(this->boolpolydev) {
103         delete this->boolpolydev;this->boolpolydev = 0;
104     }
105     if(this->booltextdev) {
106         delete this->booltextdev;this->booltextdev = 0;
107     }
108     if(this->clip0dev) {
109         delete this->clip0dev;this->clip0dev = 0;
110     }
111     if(this->clip1dev) {
112         delete this->clip1dev;this->clip1dev = 0;
113     }
114     //if(this->clipbitmap) {
115     //    delete this->clipbitmap;this->clipbitmap = 0;
116     //}
117     //if(this->clipdev) {
118     //    delete this->clipdev;this->clipdev = 0;
119     //}
120
121 }
122
123 GBool BitmapOutputDev::getVectorAntialias()
124 {
125     return this->rgbdev->getVectorAntialias();
126 }
127 void BitmapOutputDev::setVectorAntialias(GBool vaa)
128 {
129     this->rgbdev->setVectorAntialias(vaa);
130 }
131 void BitmapOutputDev::setDevice(gfxdevice_t*dev)
132 {
133     this->dev = dev;
134 }
135 void BitmapOutputDev::setMove(int x,int y)
136 {
137     this->gfxdev->setMove(x,y);
138     this->user_movex = x;
139     this->user_movey = y;
140 }
141 void BitmapOutputDev::setClip(int x1,int y1,int x2,int y2)
142 {
143     this->gfxdev->setClip(x1,y1,x2,y2);
144     this->user_clipx1 = x1;
145     this->user_clipy1 = y1;
146     this->user_clipx2 = x2;
147     this->user_clipy2 = y2;
148 }
149 void BitmapOutputDev::setParameter(const char*key, const char*value)
150 {
151     if(!strcmp(key, "extrafontdata")) {
152         this->config_extrafontdata = atoi(value);
153     }
154     this->gfxdev->setParameter(key, value);
155 }
156 void BitmapOutputDev::setPageMap(int*page2page, int num_pages)
157 {
158     this->gfxdev->setPageMap(page2page, num_pages);
159 }
160
161 typedef struct _ibbox {
162     int xmin,ymin,xmax,ymax;
163     struct _ibbox*next;
164 } ibbox_t;
165
166 static ibbox_t* ibbox_new(int x1, int y1, int x2, int y2)
167 {
168     ibbox_t*b = (ibbox_t*)rfx_calloc(sizeof(ibbox_t)); 
169     b->xmin = x1;
170     b->ymin = y1;
171     b->xmax = x2;
172     b->ymax = y2;
173     return b;
174 }
175
176 static void ibbox_destroy(ibbox_t*b)
177 {
178     while(b) {
179         ibbox_t*next = b->next;
180         free(b);
181         b = next;
182     }
183 }
184
185 static ibbox_t*getBitmapBBoxes(Guchar*alpha, int width, int height)
186 {
187     int ymin = -1;
188     int ymax = -1;
189     int xmin = width;
190     int xmax = 0;
191
192     int x,y;
193     for(y=0;y<height;y++) {
194         Guchar*a = &alpha[y*width];
195         for(x=0;x<width;x++) {
196             if(a[x]) break;
197         }
198         int left = x; //first occupied pixel from left
199         int right = x+1; //last non-occupied pixel from right
200         for(;x<width;x++) {
201             if(a[x]) right=x+1;
202         }
203
204         if(left!=width) {
205             if(ymin<0) 
206                 ymin=y;
207             ymax=y+1;
208             if(left<xmin) xmin = left;
209             if(right>xmax) xmax = right;
210         }
211     }
212     ibbox_t* bbox = 0;
213     if(xmin<xmax || ymin<ymax) {
214         bbox = ibbox_new(xmin, ymin, xmax, ymax);
215     }
216     return bbox;
217 }
218
219 void BitmapOutputDev::flushBitmap()
220 {
221     int width = rgbdev->getBitmapWidth();
222     int height = rgbdev->getBitmapHeight();
223
224     if(sizeof(SplashColor)!=3) {
225         msg("<error> sizeof(SplashColor)!=3");
226         return;
227     }
228     
229     SplashColorPtr rgb = rgbbitmap->getDataPtr();
230     Guchar*alpha = rgbbitmap->getAlphaPtr();
231     Guchar*alpha2 = boolpolybitmap->getAlphaPtr();
232
233     ibbox_t* boxes = getBitmapBBoxes(alpha, width, height);
234     ibbox_t*b;
235
236     for(b=boxes;boxes;boxes=boxes->next) {
237         int xmin = b->xmin;
238         int ymin = b->ymin;
239         int xmax = b->xmax;
240         int ymax = b->ymax;
241
242         /* clip against (-movex, -movey, -movex+width, -movey+height) */
243         if(xmin < -this->movex) xmin = -this->movex;
244         if(ymin < -this->movey) ymin = -this->movey;
245         if(xmax > -this->movex + width) xmax = -this->movex+this->width;
246         if(ymax > -this->movey + height) ymax = -this->movey+this->height;
247
248         msg("<verbose> Flushing bitmap (bbox: %d,%d,%d,%d)", xmin,ymin,xmax,ymax);
249         
250         if((xmax-xmin)<=0 || (ymax-ymin)<=0) // no bitmap, nothing to do
251             continue;
252
253         int rangex = xmax-xmin;
254         int rangey = ymax-ymin;
255         gfximage_t*img = (gfximage_t*)malloc(sizeof(gfximage_t)); 
256         img->data = (gfxcolor_t*)malloc(rangex * rangey * 4);
257         img->width = rangex;
258         img->height = rangey;
259         int x,y;
260         for(y=0;y<rangey;y++) {
261             SplashColorPtr in=&rgb[((y+ymin)*width+xmin)*sizeof(SplashColor)];
262             gfxcolor_t*out = &img->data[y*rangex];
263             Guchar*ain = &alpha[(y+ymin)*width+xmin];
264             Guchar*ain2 = &alpha2[(y+ymin)*width+xmin];
265             if(this->emptypage) {
266                 for(x=0;x<rangex;x++) {
267                     /* the first bitmap on the page doesn't need to have an alpha channel-
268                        blend against a white background*/
269                     out[x].r = (in[x*3+0]*ain[x])/255 + 255-ain[x];
270                     out[x].g = (in[x*3+1]*ain[x])/255 + 255-ain[x];
271                     out[x].b = (in[x*3+2]*ain[x])/255 + 255-ain[x];
272                     out[x].a = 255;
273                 }
274             } else {
275                 for(x=0;x<rangex;x++) {
276                     /*
277                     if(!ain2[x]) {
278                         out[x].r = 0;out[x].g = 0;out[x].b = 0;out[x].a = 0;
279                     } else */
280
281                     /* according to endPage()/compositeBackground() in xpdf/SplashOutputDev.cc, we
282                        have to premultiply alpha (mix background and pixel according to the alpha channel).
283                     */
284                     out[x].r = (in[x*3+0]*ain[x])/255;
285                     out[x].g = (in[x*3+1]*ain[x])/255;
286                     out[x].b = (in[x*3+2]*ain[x])/255;
287                     out[x].a = ain[x];
288                 }
289             }
290         }
291
292         /* transform bitmap rectangle to "device space" */
293         xmin += movex;
294         ymin += movey;
295         xmax += movex;
296         ymax += movey;
297
298         gfxmatrix_t m;
299         m.tx = xmin;
300         m.ty = ymin;
301         m.m00 = m.m11 = 1;
302         m.m10 = m.m01 = 0;
303         m.tx -= 0.5;
304         m.ty -= 0.5;
305
306         gfxline_t* line = gfxline_makerectangle(xmin, ymin, xmax, ymax);
307         dev->fillbitmap(dev, line, img, &m, 0);
308         gfxline_free(line);
309     
310         free(img->data);img->data=0;free(img);img=0;
311     }
312     ibbox_destroy(boxes);
313
314     memset(rgbbitmap->getAlphaPtr(), 0, rgbbitmap->getWidth()*rgbbitmap->getHeight());
315     memset(rgbbitmap->getDataPtr(), 0, rgbbitmap->getRowSize()*rgbbitmap->getHeight());
316
317     this->emptypage = 0;
318 }
319
320 void BitmapOutputDev::flushText()
321 {
322     msg("<verbose> Flushing text/polygons");
323     gfxdevice_record_flush(this->gfxoutput, this->dev);
324     
325     this->emptypage = 0;
326 }
327
328 void writeMonoBitmap(SplashBitmap*btm, char*filename)
329 {
330     int width8 = (btm->getWidth()+7)/8;
331     int width = btm->getWidth();
332     int height = btm->getHeight();
333     gfxcolor_t*b = (gfxcolor_t*)malloc(sizeof(gfxcolor_t)*width*height);
334     unsigned char*data = btm->getDataPtr();
335     int x,y;
336     for(y=0;y<height;y++) {
337         unsigned char*l = &data[width8*y];
338         gfxcolor_t*d = &b[width*y];
339         for(x=0;x<width;x++) {
340             if(l[x>>3]&(128>>(x&7))) {
341                 d[x].r = d[x].g = d[x].b = 255;
342             } else {
343                 d[x].r = d[x].g = d[x].b = 0;
344             }
345             d[x].a = 255;
346         }
347     }
348     writePNG(filename, (unsigned char*)b, width, height);
349     free(b);
350 }
351
352 void writeBitmap(SplashBitmap*bitmap, char*filename)
353 {
354     int y,x;
355     
356     int width = bitmap->getWidth();
357     int height = bitmap->getHeight();
358
359     gfxcolor_t*data = (gfxcolor_t*)malloc(sizeof(gfxcolor_t)*width*height);
360
361     if(bitmap->getMode()==splashModeMono1) {
362         writeMonoBitmap(bitmap, filename);
363         return;
364     }
365
366     for(y=0;y<height;y++) {
367         gfxcolor_t*line = &data[y*width];
368         for(x=0;x<width;x++) {
369             Guchar c[4] = {0,0,0,0};
370             bitmap->getPixel(x,y,c);
371             line[x].r = c[0];
372             line[x].g = c[1];
373             line[x].b = c[2];
374             line[x].a =  bitmap->getAlpha(x,y);
375         }
376     }
377     writePNG(filename, (unsigned char*)data, width, height);
378     free(data);
379 }
380
381 void writeAlpha(SplashBitmap*bitmap, char*filename)
382 {
383     int y,x;
384     
385     int width = bitmap->getWidth();
386     int height = bitmap->getHeight();
387     
388     if(bitmap->getMode()==splashModeMono1) {
389         writeMonoBitmap(bitmap, filename);
390         return;
391     }
392
393     gfxcolor_t*data = (gfxcolor_t*)malloc(sizeof(gfxcolor_t)*width*height);
394
395     for(y=0;y<height;y++) {
396         gfxcolor_t*line = &data[y*width];
397         for(x=0;x<width;x++) {
398             int a = bitmap->getAlpha(x,y);
399             line[x].r = a;
400             line[x].g = a;
401             line[x].b = a;
402             line[x].a = 255;
403         }
404     }
405     writePNG(filename, (unsigned char*)data, width, height);
406     free(data);
407 }
408 static int dbg_btm_counter=1;
409
410 static const char*STATE_NAME[] = {"parallel", "textabovebitmap", "bitmapabovetext"};
411
412 int checkAlphaSanity(SplashBitmap*boolbtm, SplashBitmap*alphabtm)
413 {
414     assert(boolbtm->getWidth() == alphabtm->getWidth());
415     assert(boolbtm->getHeight() == alphabtm->getHeight());
416     if(boolbtm->getMode()==splashModeMono1) {
417         return 1;
418     }
419
420     int width = boolbtm->getWidth();
421     int height = boolbtm->getHeight();
422
423     int bad=0;
424     int x,y;
425     for(y=0;y<height;y++) {
426         for(x=0;x<width;x++) {
427             int a1 = alphabtm->getAlpha(x,y);
428             int a2 = boolbtm->getAlpha(x,y);
429             if(a1!=a2) {
430                 bad++;
431             }
432         }
433     }
434     double badness = bad/(double)(width*height);
435     if(badness>0.2) {
436         msg("<error> Bitmaps don't correspond: %d out of %d pixels wrong (%.2f%%)", bad, width*height, 
437                 badness*100.0);
438         return 0;
439     }
440     msg("<notice> %f", badness);
441     return 1;
442 }
443
444 GBool BitmapOutputDev::checkNewText(int x1, int y1, int x2, int y2)
445 {
446     /* called once some new text was drawn on booltextdev, and
447        before the same thing is drawn on gfxdev */
448    
449     msg("<trace> Testing new text data against current bitmap data, state=%s, counter=%d\n", STATE_NAME[layerstate], dbg_btm_counter);
450     
451     if(0) {
452         char filename1[80];
453         char filename2[80];
454         char filename3[80];
455         sprintf(filename1, "state%dboolbitmap_afternewtext.png", dbg_btm_counter);
456         sprintf(filename2, "state%dbooltext_afternewtext.png", dbg_btm_counter);
457         sprintf(filename3, "state%dbitmap_afternewtext.png", dbg_btm_counter);
458         msg("<verbose> %s %s %s", filename1, filename2, filename3);
459         writeAlpha(boolpolybitmap, filename1);
460         writeAlpha(booltextbitmap, filename2);
461         writeBitmap(rgbdev->getBitmap(), filename3);
462     }
463     dbg_btm_counter++;
464
465     GBool ret = false;
466     if(intersection(x1,y1,x2,y2)) {
467         if(layerstate==STATE_PARALLEL) {
468             /* the new text is above the bitmap. So record that fact,
469                and also clear the bitmap buffer, so we can check for
470                new intersections */
471             msg("<verbose> Text is above current bitmap/polygon data");
472             layerstate=STATE_TEXT_IS_ABOVE;
473             clearBoolPolyDev(x1,y1,x2,y2);
474         } else if(layerstate==STATE_BITMAP_IS_ABOVE) {
475             /* there's a bitmap above the (old) text. So we need
476                to flush out that text, and record that the *new*
477                text is now *above* the bitmap
478              */
479             msg("<verbose> Text is above current bitmap/polygon data (which is above some other text)");
480             flushText();
481             layerstate=STATE_TEXT_IS_ABOVE;
482             /* clear both bool devices- the text device because
483                we just dumped out all the (old) text, and the
484                poly dev so we can check for new intersections */
485             clearBoolPolyDev(x1,y1,x2,y2);
486             /* FIXME this destroys the text pixels we just
487                drew to test for the intersection- however we need
488                those to check for the *new* intersections */
489             clearBoolTextDev(x1,y1,x2,y2);
490             ret = true;
491         } else {
492             /* we already know that the current text section is
493                above the current bitmap section- now just new
494                bitmap data *and* new text data was drawn, and
495                *again* it's above the current bitmap- so clear
496                the polygon bitmap again, so we can check for
497                new intersections */
498             msg("<verbose> Text is still above current bitmap/polygon data");
499             clearBoolPolyDev(x1,y1,x2,y2);
500         }
501     } 
502     return ret;
503 }
504
505 GBool BitmapOutputDev::checkNewBitmap(int x1, int y1, int x2, int y2)
506 {
507     /* similar to checkNewText() above, only in reverse */
508     msg("<trace> Testing new graphics data against current text data, state=%s, counter=%d\n", STATE_NAME[layerstate], dbg_btm_counter);
509
510     if(0) {
511         char filename1[80];
512         char filename2[80];
513         char filename3[80];
514         sprintf(filename1, "state%dboolbitmap_afternewgfx.png", dbg_btm_counter);
515         sprintf(filename2, "state%dbooltext_afternewgfx.png", dbg_btm_counter);
516         sprintf(filename3, "state%dbitmap_afternewgfx.png", dbg_btm_counter);
517         msg("<verbose> %s %s %s", filename1, filename2, filename3);
518         writeAlpha(boolpolybitmap, filename1);
519         writeAlpha(booltextbitmap, filename2);
520         writeBitmap(rgbdev->getBitmap(), filename3);
521     }
522     dbg_btm_counter++;
523
524     GBool ret = false;
525     if(intersection(x1,y1,x2,y2)) {
526         if(layerstate==STATE_PARALLEL) {
527             msg("<verbose> Bitmap is above current text data");
528             layerstate=STATE_BITMAP_IS_ABOVE;
529             clearBoolTextDev(x1,y1,x2,y2);
530         } else if(layerstate==STATE_TEXT_IS_ABOVE) {
531             msg("<verbose> Bitmap is above current text data (which is above some bitmap)");
532             flushBitmap();
533             layerstate=STATE_BITMAP_IS_ABOVE;
534             clearBoolTextDev(x1,y1,x2,y2);
535             /* FIXME this destroys the polygon pixels we just
536                drew to test for the intersection- however we need
537                those to check for the *new* intersections */
538             clearBoolPolyDev(x1,y1,x2,y2);
539             ret = true;
540         } else {
541             msg("<verbose> Bitmap is still above current text data");
542             clearBoolTextDev(x1,y1,x2,y2);
543         }
544     } 
545     return ret;
546 }
547
548 //void checkNewText() {
549 //    Guchar*alpha = rgbbitmap->getAlphaPtr();
550 //    Guchar*charpixels = clip1bitmap->getDataPtr();
551 //    int xx,yy;
552 //    for(yy=0;yy<height;yy++) {
553 //        Guchar*aline = &alpha[yy*width];
554 //        Guchar*cline = &charpixels[yy*width8];
555 //        for(xx=0;xx<width;xx++) {
556 //            int bit = xx&7;
557 //            int bytepos = xx>>3;
558 //            /* TODO: is the bit order correct? */
559 //            if(aline[xx] && (cline[bytepos]&(1<<bit))) 
560 //              break;
561 //        }
562 //        if(xx!=width)
563 //            break;
564 //}
565
566 static inline GBool fixBBox(int*x1, int*y1, int*x2, int*y2, int width, int height)
567 {
568     if(!(*x1|*y1|*x2|*y2)) {
569         // undefined bbox
570         *x1 = *y1 = 0;
571         *x2 = width;
572         *y2 = height;
573         return gTrue;
574     }
575     if(*x2<=*x1) return gFalse;
576     if(*x2<0) return gFalse;
577     if(*x1<0) *x1 = 0;
578     if(*x1>=width) return gFalse;
579     if(*x2>width) *x2=width;
580
581     if(*y2<=*y1) return gFalse;
582     if(*y2<0) return gFalse;
583     if(*y1<0) *y1 = 0;
584     if(*y1>=height) return gFalse;
585     if(*y2>height) *y2=height;
586     return gTrue;
587 }
588
589 GBool BitmapOutputDev::clip0and1differ(int x1,int y1,int x2,int y2)
590 {
591     if(clip0bitmap->getMode()==splashModeMono1) {
592         int width = clip0bitmap->getWidth();
593         int width8 = (width+7)/8;
594         int height = clip0bitmap->getHeight();
595
596         if(!fixBBox(&x1,&y1,&x2,&y2,width,height)) {
597             /* area is outside or null */
598             return gFalse;
599         }
600         
601         SplashBitmap*clip0 = clip0bitmap;
602         SplashBitmap*clip1 = clip1bitmap;
603         int x18 = x1/8;
604         int x28 = (x2+7)/8;
605         int y;
606
607         for(y=y1;y<y2;y++) {
608             unsigned char*row1 = &clip0bitmap->getDataPtr()[width8*y+x18];
609             unsigned char*row2 = &clip1bitmap->getDataPtr()[width8*y+x18];
610             if(memcmp(row1, row2, x28-x18)) {
611                 return gTrue;
612             }
613         }
614         return gFalse;
615     } else {
616         SplashBitmap*clip0 = clip0bitmap;
617         SplashBitmap*clip1 = clip1bitmap;
618         int width = clip0->getAlphaRowSize();
619         int height = clip0->getHeight();
620
621         if(!fixBBox(&x1, &y1, &x2, &y2, width, height)) {
622             x1=y1=0;x2=y2=1;
623         }
624
625         Guchar*a0 = clip0->getAlphaPtr();
626         Guchar*a1 = clip1->getAlphaPtr();
627         int x,y;
628         char differs=0;
629         for(y=y1;y<y2;y++) {
630             for(x=x1;x<x2;x++) {
631                 if(a0[y*width+x]!=a1[y*width+x]) {
632                     differs=1;
633                     break;
634                 }
635             }
636             if(differs)
637                 break;
638         }
639         char differs2 = memcmp(a0, a1, width*height);
640         if(differs && !differs2) 
641             msg("<warning> Strange internal error (2)");
642         else if(!differs && differs2) {
643             msg("<warning> Bad Bounding Box: Difference in clip0 and clip1 outside bbox");
644             msg("<warning> %d %d %d %d", x1, y1, x2, y2);
645         }
646         return differs2;
647     }
648 }
649
650 static void clearBooleanBitmap(SplashBitmap*btm, int x1, int y1, int x2, int y2)
651 {
652     if(!(x1|y1|x2|y2)) {
653         x1 = y1 = 0;
654         x2 = btm->getWidth();
655         y2 = btm->getHeight();
656     }
657     if(btm->getMode()==splashModeMono1) {
658         int width8 = (btm->getWidth()+7)/8;
659         int width = btm->getWidth();
660         int height = btm->getHeight();
661         memset(btm->getDataPtr(), 0, width8*height);
662     } else {
663         int width = btm->getAlphaRowSize();
664         int height = btm->getHeight();
665         memset(btm->getAlphaPtr(), 0, width*height);
666     }
667 }
668
669 GBool compare8(unsigned char*data1, unsigned char*data2, int len)
670 {
671     if(!len)
672         return 0;
673     if(((ptroff_t)data1&7)==((ptroff_t)data2&7)) {
674         // oh good, we can do aligning
675         while((ptroff_t)data1&7) {
676             if(*data1&*data2)
677                 return 1;
678             data1++;
679             data2++;
680             if(!--len)
681                 return 0;
682         }
683     }
684     /* use 64 bit for the (hopefully aligned) middle section */
685     int l8 = len/8;
686     long long unsigned int*d1 = (long long unsigned int*)data1;
687     long long unsigned int*d2 = (long long unsigned int*)data2;
688     long long unsigned int x = 0;
689     int t;
690     for(t=0;t<l8;t++) {
691         x |= d1[t]&d2[t];
692     }
693     if(x)
694         return 1;
695
696     data1+=l8*8;
697     data2+=l8*8;
698     len -= l8*8;
699     for(t=0;t<len;t++) {
700         if(data1[t]&data2[t]) {
701             return 1;
702         }
703     }
704     return 0;
705 }
706
707 GBool BitmapOutputDev::intersection(int x1, int y1, int x2, int y2)
708 {
709     SplashBitmap*boolpoly = boolpolybitmap;
710     SplashBitmap*booltext = booltextbitmap;
711             
712     if(boolpoly->getMode()==splashModeMono1) {
713         /* alternative implementation, using one bit per pixel-
714            needs the no-dither patch in xpdf */
715         
716         int width = boolpoly->getWidth();
717         int height = boolpoly->getHeight();
718
719         if(!fixBBox(&x1,&y1,&x2,&y2, width, height)) {
720             return gFalse;
721         }
722
723         Guchar*polypixels = boolpoly->getDataPtr();
724         Guchar*textpixels = booltext->getDataPtr();
725
726         int width8 = (width+7)/8;
727         int runx = width8;
728         int runy = height;
729         
730         if(x1|y1|x2|y2) {
731             polypixels+=y1*width8+x1/8;
732             textpixels+=y1*width8+x1/8;
733             runx=(x2+7)/8 - x1/8;
734             runy=y2-y1;
735         }
736         
737         int t;
738         unsigned char c=0;
739         
740         /*assert(sizeof(unsigned long long int)==8);
741         if(((ptroff_t)polypixels&7) || ((ptroff_t)textpixels&7)) {
742             //msg("<warning> Non-optimal alignment");
743         }*/
744
745         int x,y;
746         unsigned char*data1 = (unsigned char*)polypixels;
747         unsigned char*data2 = (unsigned char*)textpixels;
748         msg("<verbose> Testing area (%d,%d,%d,%d), runx=%d,runy=%d", x1,y1,x2,y2, runx, runy);
749         for(y=0;y<runy;y++) {
750             if(compare8(data1,data2,runx))
751                 return gTrue;
752             data1+=width8;
753             data2+=width8;
754         }
755         return gFalse;
756     } else {
757         int width = boolpoly->getAlphaRowSize();
758         int height = boolpoly->getHeight();
759
760         if(!fixBBox(&x1, &y1, &x2, &y2, width, height)) {
761             x1=y1=0;x2=y2=1;
762         }
763         Guchar*polypixels = boolpoly->getAlphaPtr();
764         Guchar*textpixels = booltext->getAlphaPtr();
765
766         int x,y;
767         char overlap1 = 0;
768         char overlap2 = 0;
769         for(x=x1;x<x2;x++) {
770             for(y=y1;y<y2;y++) {
771                 if(polypixels[width*y+x]&&textpixels[width*y+x])
772                     overlap1 = 1;
773             }
774         }
775         int ax1=0,ay1=0,ax2=0,ay2=0;
776         for(y=0;y<height;y++) {
777             for(x=0;x<width;x++) {
778                 if(polypixels[width*y+x]&&textpixels[width*y+x]) {
779                     overlap2 = 1;
780                     if(!(ax1|ay1|ax2|ay2)) {
781                         ax1 = ax2 = x;
782                         ay1 = ay2 = y;
783                     } else {
784                         ax1 = ax1<x?ax1:x;
785                         ay1 = ay1<y?ay1:y;
786                         ax2 = ax2>x?ax2:x;
787                         ay2 = ay2>y?ay2:y;
788                     }
789                 }
790             }
791         }
792         if(overlap1 && !overlap2)
793             msg("<warning> strange internal error");
794         if(!overlap1 && overlap2) {
795             msg("<warning> Bad bounding box: intersection outside bbox");
796             msg("<warning> given bbox: %d %d %d %d", x1, y1, x2, y2);
797             msg("<warning> changed area: %d %d %d %d", ax1, ay1, ax2, ay2);
798             //writeAlpha(booltextbitmap, "alpha.png");
799         }
800         return overlap2;
801     }
802 }
803
804
805 void BitmapOutputDev::startPage(int pageNum, GfxState *state, double crop_x1, double crop_y1, double crop_x2, double crop_y2)
806 {
807     double x1,y1,x2,y2;
808     state->transform(crop_x1,crop_y1,&x1,&y1);
809     state->transform(crop_x2,crop_y2,&x2,&y2);
810     if(x2<x1) {double x3=x1;x1=x2;x2=x3;}
811     if(y2<y1) {double y3=y1;y1=y2;y2=y3;}
812     
813     this->movex = -(int)x1 - user_movex;
814     this->movey = -(int)y1 - user_movey;
815     
816     if(user_clipx1|user_clipy1|user_clipx2|user_clipy2) {
817         x1 = user_clipx1;
818         x2 = user_clipx2;
819         y1 = user_clipy1;
820         y2 = user_clipy2;
821     }
822     this->width = (int)(x2-x1);
823     this->height = (int)(y2-y1);
824
825     msg("<debug> startPage");
826     rgbdev->startPage(pageNum, state, crop_x1, crop_y1, crop_x2, crop_y2);
827     boolpolydev->startPage(pageNum, state, crop_x1, crop_y1, crop_x2, crop_y2);
828     booltextdev->startPage(pageNum, state, crop_x1, crop_y1, crop_x2, crop_y2);
829     clip0dev->startPage(pageNum, state, crop_x1, crop_y1, crop_x2, crop_y2);
830     clip1dev->startPage(pageNum, state, crop_x1, crop_y1, crop_x2, crop_y2);
831     gfxdev->startPage(pageNum, state, crop_x1, crop_y1, crop_x2, crop_y2);
832
833     boolpolybitmap = boolpolydev->getBitmap();
834     booltextbitmap = booltextdev->getBitmap();
835     clip0bitmap = clip0dev->getBitmap();
836     clip1bitmap = clip1dev->getBitmap();
837     rgbbitmap = rgbdev->getBitmap();
838     
839     flushText(); // write out the initial clipping rectangle
840
841     /* just in case any device did draw a white background rectangle 
842        into the device */
843     clearBoolTextDev(UNKNOWN_BOUNDING_BOX);
844     clearBoolPolyDev(UNKNOWN_BOUNDING_BOX);
845
846     this->layerstate = STATE_PARALLEL;
847     this->emptypage = 1;
848     msg("<debug> startPage done");
849 }
850
851 void BitmapOutputDev::endPage()
852 {
853     msg("<verbose> endPage (BitmapOutputDev)");
854
855     /* notice: we're not fully done yet with this page- there might still be 
856        a few calls to drawLink() yet to come */
857 }
858 void BitmapOutputDev::finishPage()
859 {
860     msg("<verbose> finishPage (BitmapOutputDev)");
861     gfxdev->endPage();
862    
863     if(layerstate == STATE_BITMAP_IS_ABOVE) {
864         this->flushText();
865         this->flushBitmap();
866     } else {
867         this->flushBitmap();
868         this->flushText();
869     }
870
871     /* splash will now destroy alpha, and paint the 
872        background color into the "holes" in the bitmap */
873     boolpolydev->endPage();
874     booltextdev->endPage();
875     rgbdev->endPage();
876     clip0dev->endPage();
877     clip1dev->endPage();
878 }
879
880 GBool BitmapOutputDev::upsideDown()
881 {
882     boolpolydev->upsideDown();
883     booltextdev->upsideDown();
884     clip0dev->upsideDown();
885     clip1dev->upsideDown();
886     return rgbdev->upsideDown();
887 }
888
889 GBool BitmapOutputDev::useDrawChar()
890 {
891     boolpolydev->useDrawChar();
892     booltextdev->useDrawChar();
893     clip0dev->useDrawChar();
894     clip1dev->useDrawChar();
895     return rgbdev->useDrawChar();
896 }
897
898 GBool BitmapOutputDev::useTilingPatternFill()
899 {
900     boolpolydev->useTilingPatternFill();
901     booltextdev->useTilingPatternFill();
902     clip0dev->useTilingPatternFill();
903     clip1dev->useTilingPatternFill();
904     return rgbdev->useTilingPatternFill();
905 }
906
907 GBool BitmapOutputDev::useShadedFills()
908 {
909     boolpolydev->useShadedFills();
910     booltextdev->useShadedFills();
911     clip0dev->useShadedFills();
912     clip1dev->useShadedFills();
913     return rgbdev->useShadedFills();
914 }
915
916 GBool BitmapOutputDev::useDrawForm()
917 {
918     boolpolydev->useDrawForm();
919     booltextdev->useDrawForm();
920     clip0dev->useDrawForm();
921     clip1dev->useDrawForm();
922     return rgbdev->useDrawForm();
923 }
924
925 GBool BitmapOutputDev::interpretType3Chars()
926 {
927     boolpolydev->interpretType3Chars();
928     booltextdev->interpretType3Chars();
929     clip0dev->interpretType3Chars();
930     clip1dev->interpretType3Chars();
931     return rgbdev->interpretType3Chars();
932 }
933
934 GBool BitmapOutputDev::needNonText() 
935 {
936     boolpolydev->needNonText();
937     booltextdev->needNonText();
938     clip0dev->needNonText();
939     clip1dev->needNonText();
940     return rgbdev->needNonText();
941 }
942 /*GBool BitmapOutputDev::checkPageSlice(Page *page, double hDPI, double vDPI,
943                            int rotate, GBool useMediaBox, GBool crop,
944                            int sliceX, int sliceY, int sliceW, int sliceH,
945                            GBool printing, Catalog *catalog,
946                            GBool (*abortCheckCbk)(void *data),
947                            void *abortCheckCbkData)
948 {
949     return gTrue;
950 }*/
951 void BitmapOutputDev::setDefaultCTM(double *ctm) 
952 {
953     boolpolydev->setDefaultCTM(ctm);
954     booltextdev->setDefaultCTM(ctm);
955     rgbdev->setDefaultCTM(ctm);
956     clip0dev->setDefaultCTM(ctm);
957     clip1dev->setDefaultCTM(ctm);
958     gfxdev->setDefaultCTM(ctm);
959 }
960 void BitmapOutputDev::saveState(GfxState *state) 
961 {
962     boolpolydev->saveState(state);
963     booltextdev->saveState(state);
964     rgbdev->saveState(state);
965     clip0dev->saveState(state);
966     clip1dev->saveState(state);
967
968     /*ClipState*cstate = new ClipState();
969     cstate->next = this->clipstates;
970     this->clipstates = cstate;*/
971 }
972 void BitmapOutputDev::restoreState(GfxState *state) 
973 {
974     boolpolydev->restoreState(state);
975     booltextdev->restoreState(state);
976     rgbdev->restoreState(state);
977     clip0dev->restoreState(state);
978     clip1dev->restoreState(state);
979
980     /*if(this->clipstates) {
981         ClipState*old = this->clipstates;
982         if(old->written) {
983             gfxdev->restoreState(state);
984         }
985         this->clipstates = this->clipstates->next;
986         delete(old);
987     } else {
988         msg("<error> invalid restoreState()");
989     }*/
990 }
991 void BitmapOutputDev::updateAll(GfxState *state)
992 {
993     boolpolydev->updateAll(state);
994     booltextdev->updateAll(state);
995     rgbdev->updateAll(state);
996     clip0dev->updateAll(state);
997     clip1dev->updateAll(state);
998     gfxdev->updateAll(state);
999 }
1000 void BitmapOutputDev::updateCTM(GfxState *state, double m11, double m12, double m21, double m22, double m31, double m32)
1001 {
1002     boolpolydev->updateCTM(state,m11,m12,m21,m22,m31,m32);
1003     booltextdev->updateCTM(state,m11,m12,m21,m22,m31,m32);
1004     rgbdev->updateCTM(state,m11,m12,m21,m22,m31,m32);
1005     clip0dev->updateCTM(state,m11,m12,m21,m22,m31,m32);
1006     clip1dev->updateCTM(state,m11,m12,m21,m22,m31,m32);
1007     gfxdev->updateCTM(state,m11,m12,m21,m22,m31,m32);
1008 }
1009 void BitmapOutputDev::updateLineDash(GfxState *state)
1010 {
1011     boolpolydev->updateLineDash(state);
1012     booltextdev->updateLineDash(state);
1013     rgbdev->updateLineDash(state);
1014     clip0dev->updateLineDash(state);
1015     clip1dev->updateLineDash(state);
1016     gfxdev->updateLineDash(state);
1017 }
1018 void BitmapOutputDev::updateFlatness(GfxState *state)
1019 {
1020     boolpolydev->updateFlatness(state);
1021     booltextdev->updateFlatness(state);
1022     rgbdev->updateFlatness(state);
1023     clip0dev->updateFlatness(state);
1024     clip1dev->updateFlatness(state);
1025     gfxdev->updateFlatness(state);
1026 }
1027 void BitmapOutputDev::updateLineJoin(GfxState *state)
1028 {
1029     boolpolydev->updateLineJoin(state);
1030     booltextdev->updateLineJoin(state);
1031     rgbdev->updateLineJoin(state);
1032     clip0dev->updateLineJoin(state);
1033     clip1dev->updateLineJoin(state);
1034     gfxdev->updateLineJoin(state);
1035 }
1036 void BitmapOutputDev::updateLineCap(GfxState *state)
1037 {
1038     boolpolydev->updateLineCap(state);
1039     booltextdev->updateLineCap(state);
1040     rgbdev->updateLineCap(state);
1041     clip0dev->updateLineCap(state);
1042     clip1dev->updateLineCap(state);
1043     gfxdev->updateLineCap(state);
1044 }
1045 void BitmapOutputDev::updateMiterLimit(GfxState *state)
1046 {
1047     boolpolydev->updateMiterLimit(state);
1048     booltextdev->updateMiterLimit(state);
1049     rgbdev->updateMiterLimit(state);
1050     clip0dev->updateMiterLimit(state);
1051     clip1dev->updateMiterLimit(state);
1052     gfxdev->updateMiterLimit(state);
1053 }
1054 void BitmapOutputDev::updateLineWidth(GfxState *state)
1055 {
1056     boolpolydev->updateLineWidth(state);
1057     booltextdev->updateLineWidth(state);
1058     rgbdev->updateLineWidth(state);
1059     clip0dev->updateLineWidth(state);
1060     clip1dev->updateLineWidth(state);
1061     gfxdev->updateLineWidth(state);
1062 }
1063 void BitmapOutputDev::updateStrokeAdjust(GfxState *state)
1064 {
1065     boolpolydev->updateStrokeAdjust(state);
1066     booltextdev->updateStrokeAdjust(state);
1067     rgbdev->updateStrokeAdjust(state);
1068     clip0dev->updateStrokeAdjust(state);
1069     clip1dev->updateStrokeAdjust(state);
1070     gfxdev->updateStrokeAdjust(state);
1071 }
1072 void BitmapOutputDev::updateFillColorSpace(GfxState *state)
1073 {
1074     boolpolydev->updateFillColorSpace(state);
1075     booltextdev->updateFillColorSpace(state);
1076     rgbdev->updateFillColorSpace(state);
1077     clip0dev->updateFillColorSpace(state);
1078     clip1dev->updateFillColorSpace(state);
1079     gfxdev->updateFillColorSpace(state);
1080 }
1081 void BitmapOutputDev::updateStrokeColorSpace(GfxState *state)
1082 {
1083     boolpolydev->updateStrokeColorSpace(state);
1084     booltextdev->updateStrokeColorSpace(state);
1085     rgbdev->updateStrokeColorSpace(state);
1086     clip0dev->updateStrokeColorSpace(state);
1087     clip1dev->updateStrokeColorSpace(state);
1088     gfxdev->updateStrokeColorSpace(state);
1089 }
1090 void BitmapOutputDev::updateFillColor(GfxState *state)
1091 {
1092     boolpolydev->updateFillColor(state);
1093     booltextdev->updateFillColor(state);
1094     rgbdev->updateFillColor(state);
1095     clip0dev->updateFillColor(state);
1096     clip1dev->updateFillColor(state);
1097     gfxdev->updateFillColor(state);
1098 }
1099 void BitmapOutputDev::updateStrokeColor(GfxState *state)
1100 {
1101     boolpolydev->updateStrokeColor(state);
1102     booltextdev->updateStrokeColor(state);
1103     rgbdev->updateStrokeColor(state);
1104     clip0dev->updateStrokeColor(state);
1105     clip1dev->updateStrokeColor(state);
1106     gfxdev->updateStrokeColor(state);
1107 }
1108 void BitmapOutputDev::updateBlendMode(GfxState *state)
1109 {
1110     boolpolydev->updateBlendMode(state);
1111     booltextdev->updateBlendMode(state);
1112     rgbdev->updateBlendMode(state);
1113     clip0dev->updateBlendMode(state);
1114     clip1dev->updateBlendMode(state);
1115     gfxdev->updateBlendMode(state);
1116 }
1117 void BitmapOutputDev::updateFillOpacity(GfxState *state)
1118 {
1119     boolpolydev->updateFillOpacity(state);
1120     booltextdev->updateFillOpacity(state);
1121     rgbdev->updateFillOpacity(state);
1122     clip0dev->updateFillOpacity(state);
1123     clip1dev->updateFillOpacity(state);
1124     gfxdev->updateFillOpacity(state);
1125 }
1126 void BitmapOutputDev::updateStrokeOpacity(GfxState *state)
1127 {
1128     boolpolydev->updateStrokeOpacity(state);
1129     booltextdev->updateStrokeOpacity(state);
1130     rgbdev->updateStrokeOpacity(state);
1131     clip0dev->updateStrokeOpacity(state);
1132     clip1dev->updateStrokeOpacity(state);
1133     gfxdev->updateStrokeOpacity(state);
1134 }
1135 void BitmapOutputDev::updateFillOverprint(GfxState *state)
1136 {
1137     boolpolydev->updateFillOverprint(state);
1138     booltextdev->updateFillOverprint(state);
1139     rgbdev->updateFillOverprint(state);
1140     clip0dev->updateFillOverprint(state);
1141     clip1dev->updateFillOverprint(state);
1142     gfxdev->updateFillOverprint(state);
1143 }
1144 void BitmapOutputDev::updateStrokeOverprint(GfxState *state)
1145 {
1146     boolpolydev->updateStrokeOverprint(state);
1147     booltextdev->updateStrokeOverprint(state);
1148     rgbdev->updateStrokeOverprint(state);
1149     clip0dev->updateStrokeOverprint(state);
1150     clip1dev->updateStrokeOverprint(state);
1151     gfxdev->updateStrokeOverprint(state);
1152 }
1153 void BitmapOutputDev::updateTransfer(GfxState *state)
1154 {
1155     boolpolydev->updateTransfer(state);
1156     booltextdev->updateTransfer(state);
1157     rgbdev->updateTransfer(state);
1158     clip0dev->updateTransfer(state);
1159     clip1dev->updateTransfer(state);
1160     gfxdev->updateTransfer(state);
1161 }
1162
1163 void BitmapOutputDev::updateFont(GfxState *state)
1164 {
1165     boolpolydev->updateFont(state);
1166     booltextdev->updateFont(state);
1167     rgbdev->updateFont(state);
1168     clip0dev->updateFont(state);
1169     clip1dev->updateFont(state);
1170     gfxdev->updateFont(state);
1171 }
1172 void BitmapOutputDev::updateTextMat(GfxState *state)
1173 {
1174     boolpolydev->updateTextMat(state);
1175     booltextdev->updateTextMat(state);
1176     rgbdev->updateTextMat(state);
1177     clip0dev->updateTextMat(state);
1178     clip1dev->updateTextMat(state);
1179     gfxdev->updateTextMat(state);
1180 }
1181 void BitmapOutputDev::updateCharSpace(GfxState *state)
1182 {
1183     boolpolydev->updateCharSpace(state);
1184     booltextdev->updateCharSpace(state);
1185     rgbdev->updateCharSpace(state);
1186     clip0dev->updateCharSpace(state);
1187     clip1dev->updateCharSpace(state);
1188     gfxdev->updateCharSpace(state);
1189 }
1190 void BitmapOutputDev::updateRender(GfxState *state)
1191 {
1192     boolpolydev->updateRender(state);
1193     booltextdev->updateRender(state);
1194     rgbdev->updateRender(state);
1195     clip0dev->updateRender(state);
1196     clip1dev->updateRender(state);
1197     gfxdev->updateRender(state);
1198 }
1199 void BitmapOutputDev::updateRise(GfxState *state)
1200 {
1201     boolpolydev->updateRise(state);
1202     booltextdev->updateRise(state);
1203     rgbdev->updateRise(state);
1204     clip0dev->updateRise(state);
1205     clip1dev->updateRise(state);
1206     gfxdev->updateRise(state);
1207 }
1208 void BitmapOutputDev::updateWordSpace(GfxState *state)
1209 {
1210     boolpolydev->updateWordSpace(state);
1211     booltextdev->updateWordSpace(state);
1212     rgbdev->updateWordSpace(state);
1213     clip0dev->updateWordSpace(state);
1214     clip1dev->updateWordSpace(state);
1215     gfxdev->updateWordSpace(state);
1216 }
1217 void BitmapOutputDev::updateHorizScaling(GfxState *state)
1218 {
1219     boolpolydev->updateHorizScaling(state);
1220     booltextdev->updateHorizScaling(state);
1221     rgbdev->updateHorizScaling(state);
1222     clip0dev->updateHorizScaling(state);
1223     clip1dev->updateHorizScaling(state);
1224     gfxdev->updateHorizScaling(state);
1225 }
1226 void BitmapOutputDev::updateTextPos(GfxState *state)
1227 {
1228     boolpolydev->updateTextPos(state);
1229     booltextdev->updateTextPos(state);
1230     rgbdev->updateTextPos(state);
1231     clip0dev->updateTextPos(state);
1232     clip1dev->updateTextPos(state);
1233     gfxdev->updateTextPos(state);
1234 }
1235 void BitmapOutputDev::updateTextShift(GfxState *state, double shift)
1236 {
1237     boolpolydev->updateTextShift(state, shift);
1238     booltextdev->updateTextShift(state, shift);
1239     rgbdev->updateTextShift(state, shift);
1240     clip0dev->updateTextShift(state, shift);
1241     clip1dev->updateTextShift(state, shift);
1242     gfxdev->updateTextShift(state, shift);
1243 }
1244
1245 double max(double x, double y) 
1246 {
1247     return x>y?x:y;
1248 }
1249 double min(double x, double y) 
1250 {
1251     return x<y?x:y;
1252 }
1253
1254 gfxbbox_t BitmapOutputDev::getBBox(GfxState*state)
1255 {
1256     GfxPath * path = state->getPath();
1257     int num = path->getNumSubpaths();
1258     gfxbbox_t bbox = {0,0,1,1};
1259     char valid=0;
1260     int t;
1261     for(t = 0; t < num; t++) {
1262         GfxSubpath *subpath = path->getSubpath(t);
1263         int subnum = subpath->getNumPoints();
1264         int s;
1265         for(s=0;s<subnum;s++) {
1266            double x,y;
1267            state->transform(subpath->getX(s),subpath->getY(s),&x,&y);
1268            if(!valid) {
1269                bbox.xmin = x; bbox.ymin = y;
1270                bbox.xmax = x; bbox.ymax = y;
1271                valid = 1;
1272            } else {
1273                bbox.xmin = min(bbox.xmin, x);
1274                bbox.ymin = min(bbox.ymin, y);
1275                bbox.xmax = max(bbox.xmax, x);
1276                bbox.ymax = max(bbox.ymax, y);
1277            }
1278         }
1279     }
1280     return bbox;
1281 }
1282
1283 void BitmapOutputDev::stroke(GfxState *state)
1284 {
1285     msg("<debug> stroke");
1286     boolpolydev->stroke(state);
1287     gfxbbox_t bbox = getBBox(state);
1288     double width = state->getTransformedLineWidth();
1289     bbox.xmin -= width; bbox.ymin -= width;
1290     bbox.xmax += width; bbox.ymax += width;
1291     checkNewBitmap(bbox.xmin, bbox.ymin, ceil(bbox.xmax), ceil(bbox.ymax));
1292     rgbdev->stroke(state);
1293 }
1294 void BitmapOutputDev::fill(GfxState *state)
1295 {
1296     msg("<debug> fill");
1297     boolpolydev->fill(state);
1298     gfxbbox_t bbox = getBBox(state);
1299     if(checkNewBitmap(bbox.xmin, bbox.ymin, ceil(bbox.xmax), ceil(bbox.ymax))) {
1300         boolpolydev->fill(state);
1301     }
1302     rgbdev->fill(state);
1303 }
1304 void BitmapOutputDev::eoFill(GfxState *state)
1305 {
1306     msg("<debug> eoFill");
1307     boolpolydev->eoFill(state);
1308     gfxbbox_t bbox = getBBox(state);
1309     if(checkNewBitmap(bbox.xmin, bbox.ymin, ceil(bbox.xmax), ceil(bbox.ymax))) {
1310         boolpolydev->eoFill(state);
1311     }
1312     rgbdev->eoFill(state);
1313 }
1314 #if (xpdfMajorVersion*10000 + xpdfMinorVersion*100 + xpdfUpdateVersion) < 30207
1315 void BitmapOutputDev::tilingPatternFill(GfxState *state, Object *str,
1316                                int paintType, Dict *resDict,
1317                                double *mat, double *bbox,
1318                                int x0, int y0, int x1, int y1,
1319                                double xStep, double yStep)
1320 {
1321     msg("<debug> tilingPatternFill");
1322     boolpolydev->tilingPatternFill(state, str, paintType, resDict, mat, bbox, x0, y0, x1, y1, xStep, yStep);
1323     checkNewBitmap(UNKNOWN_BOUNDING_BOX);
1324     rgbdev->tilingPatternFill(state, str, paintType, resDict, mat, bbox, x0, y0, x1, y1, xStep, yStep);
1325 }
1326 #else
1327 void BitmapOutputDev::tilingPatternFill(GfxState *state, Gfx *gfx, Object *str,
1328                                int paintType, Dict *resDict,
1329                                double *mat, double *bbox,
1330                                int x0, int y0, int x1, int y1,
1331                                double xStep, double yStep) 
1332 {
1333     msg("<debug> tilingPatternFill");
1334     boolpolydev->tilingPatternFill(state, gfx, str, paintType, resDict, mat, bbox, x0, y0, x1, y1, xStep, yStep);
1335     checkNewBitmap(UNKNOWN_BOUNDING_BOX);
1336     rgbdev->tilingPatternFill(state, gfx, str, paintType, resDict, mat, bbox, x0, y0, x1, y1, xStep, yStep);
1337 }
1338 #endif
1339
1340 GBool BitmapOutputDev::functionShadedFill(GfxState *state, GfxFunctionShading *shading) 
1341 {
1342     msg("<debug> functionShadedFill");
1343     boolpolydev->functionShadedFill(state, shading);
1344     checkNewBitmap(UNKNOWN_BOUNDING_BOX);
1345     return rgbdev->functionShadedFill(state, shading);
1346 }
1347 GBool BitmapOutputDev::axialShadedFill(GfxState *state, GfxAxialShading *shading)
1348 {
1349     msg("<debug> axialShadedFill");
1350     boolpolydev->axialShadedFill(state, shading);
1351     checkNewBitmap(UNKNOWN_BOUNDING_BOX);
1352     return rgbdev->axialShadedFill(state, shading);
1353 }
1354 GBool BitmapOutputDev::radialShadedFill(GfxState *state, GfxRadialShading *shading)
1355 {
1356     msg("<debug> radialShadedFill");
1357     boolpolydev->radialShadedFill(state, shading);
1358     checkNewBitmap(UNKNOWN_BOUNDING_BOX);
1359     return rgbdev->radialShadedFill(state, shading);
1360 }
1361
1362 SplashColor black = {0,0,0};
1363 SplashColor white = {255,255,255};
1364
1365 void BitmapOutputDev::clip(GfxState *state)
1366 {
1367     msg("<debug> clip");
1368     boolpolydev->clip(state);
1369     booltextdev->clip(state);
1370     rgbdev->clip(state);
1371     clip1dev->clip(state);
1372 }
1373 void BitmapOutputDev::eoClip(GfxState *state)
1374 {
1375     msg("<debug> eoClip");
1376     boolpolydev->eoClip(state);
1377     booltextdev->eoClip(state);
1378     rgbdev->eoClip(state);
1379     clip1dev->eoClip(state);
1380 }
1381 void BitmapOutputDev::clipToStrokePath(GfxState *state)
1382 {
1383     msg("<debug> clipToStrokePath");
1384     boolpolydev->clipToStrokePath(state);
1385     booltextdev->clipToStrokePath(state);
1386     rgbdev->clipToStrokePath(state);
1387     clip1dev->clipToStrokePath(state);
1388 }
1389
1390 void BitmapOutputDev::beginStringOp(GfxState *state)
1391 {
1392     msg("<debug> beginStringOp");
1393     clip0dev->beginStringOp(state);
1394     clip1dev->beginStringOp(state);
1395     booltextdev->beginStringOp(state);
1396     gfxdev->beginStringOp(state);
1397 }
1398 void BitmapOutputDev::beginString(GfxState *state, GString *s)
1399 {
1400     msg("<debug> beginString");
1401     clip0dev->beginString(state, s);
1402     clip1dev->beginString(state, s);
1403     booltextdev->beginString(state, s);
1404     gfxdev->beginString(state, s);
1405 }
1406
1407 void BitmapOutputDev::clearClips()
1408 {
1409     clearBooleanBitmap(clip0bitmap, 0, 0, 0, 0);
1410     clearBooleanBitmap(clip1bitmap, 0, 0, 0, 0);
1411 }
1412 void BitmapOutputDev::clearBoolPolyDev(int x1, int y1, int x2, int y2)
1413 {
1414     clearBooleanBitmap(boolpolybitmap, x1, y1, x2, y2);
1415 }
1416 void BitmapOutputDev::clearBoolTextDev(int x1, int y1, int x2, int y2)
1417 {
1418     clearBooleanBitmap(booltextbitmap, x1, y1, x2, y2);
1419 }
1420 void BitmapOutputDev::drawChar(GfxState *state, double x, double y,
1421                       double dx, double dy,
1422                       double originX, double originY,
1423                       CharCode code, int nBytes, Unicode *u, int uLen)
1424 {
1425     msg("<debug> drawChar render=%d", state->getRender());
1426
1427     if(state->getRender()&RENDER_CLIP) {
1428         //char is just a clipping boundary
1429         rgbdev->drawChar(state, x, y, dx, dy, originX, originY, code, nBytes, u, uLen);
1430         boolpolydev->drawChar(state, x, y, dx, dy, originX, originY, code, nBytes, u, uLen);
1431         booltextdev->drawChar(state, x, y, dx, dy, originX, originY, code, nBytes, u, uLen);
1432         clip1dev->drawChar(state, x, y, dx, dy, originX, originY, code, nBytes, u, uLen);
1433     } else if(rgbbitmap != rgbdev->getBitmap()) {
1434         // we're doing softmasking or transparency grouping
1435         boolpolydev->drawChar(state, x, y, dx, dy, originX, originY, code, nBytes, u, uLen);
1436         //checkNewBitmap(UNKNOWN_BOUNDING_BOX);
1437         rgbdev->drawChar(state, x, y, dx, dy, originX, originY, code, nBytes, u, uLen);
1438     } else {
1439         // we're drawing a regular char
1440         clearClips();
1441         clip0dev->drawChar(state, x, y, dx, dy, originX, originY, code, nBytes, u, uLen);
1442         clip1dev->drawChar(state, x, y, dx, dy, originX, originY, code, nBytes, u, uLen);
1443    
1444         /* calculate the bbox of this character */
1445         int x1 = (int)x, x2 = (int)x+1, y1 = (int)y, y2 = (int)y+1;
1446         SplashFont*font = clip0dev->getCurrentFont();
1447         SplashPath*path = font?font->getGlyphPath(code):NULL;
1448
1449         if(!path) {
1450             if(code)
1451                 msg("<error> couldn't create outline for char %d", code);
1452             return;
1453         }
1454         x-=originX;
1455         y-=originY;
1456         path->offset((SplashCoord)x, (SplashCoord)y);
1457         int t;
1458         for(t=0;t<path->getLength();t++) {
1459             double xx,yy;
1460             Guchar f;
1461             path->getPoint(t,&xx,&yy,&f);
1462             state->transform(xx,yy,&xx,&yy);
1463             if(xx<x1) x1=(int)xx;
1464             if(yy<y1) y1=(int)yy;
1465             if(xx>=x2) x2=(int)xx+1;
1466             if(yy>=y2) y2=(int)yy+1;
1467         }
1468
1469         /* if this character is affected somehow by the various clippings (i.e., it looks
1470            different on a device without clipping), then draw it on the bitmap, not as
1471            text */
1472         if(clip0and1differ(x1,y1,x2,y2)) {
1473             msg("<verbose> Char %d is affected by clipping", code);
1474             boolpolydev->drawChar(state, x, y, dx, dy, originX, originY, code, nBytes, u, uLen);
1475             checkNewBitmap(x1,y1,x2,y2);
1476             rgbdev->drawChar(state, x, y, dx, dy, originX, originY, code, nBytes, u, uLen);
1477             if(config_extrafontdata) {
1478                 int oldrender = state->getRender();
1479                 state->setRender(3); //invisible
1480                 gfxdev->drawChar(state, x, y, dx, dy, originX, originY, code, nBytes, u, uLen);
1481                 state->setRender(oldrender);
1482             }
1483         } else {
1484
1485             /* this char is not at all affected by clipping. 
1486                Now just dump out the bitmap we're currently working on, if necessary. */
1487             booltextdev->drawChar(state, x, y, dx, dy, originX, originY, code, nBytes, u, uLen);
1488             checkNewText(x1,y1,x2,y2);
1489             /* use polygonal output device to do the actual text handling */
1490             gfxdev->drawChar(state, x, y, dx, dy, originX, originY, code, nBytes, u, uLen);
1491         }
1492     }
1493 }
1494 void BitmapOutputDev::drawString(GfxState *state, GString *s)
1495 {
1496     msg("<error> internal error: drawString not implemented");
1497     return;
1498     clip0dev->drawString(state, s);
1499     clip1dev->drawString(state, s);
1500     booltextdev->drawString(state, s);
1501     gfxdev->drawString(state, s);
1502 }
1503 void BitmapOutputDev::endTextObject(GfxState *state)
1504 {
1505     msg("<debug> endTextObject");
1506     rgbdev->endTextObject(state);
1507     clip0dev->endTextObject(state);
1508     clip1dev->endTextObject(state);
1509     booltextdev->endTextObject(state);
1510     /* the only thing "drawn" here is clipping */
1511     //checkNewText(UNKNOWN_BOUNDING_BOX);
1512     gfxdev->endTextObject(state);
1513 }
1514 void BitmapOutputDev::endString(GfxState *state)
1515 {
1516     msg("<debug> endString");
1517     clip0dev->endString(state);
1518     clip1dev->endString(state);
1519     booltextdev->endString(state);
1520     int render = state->getRender();
1521     if(render != RENDER_INVISIBLE && render != RENDER_FILL) {
1522         checkNewText(UNKNOWN_BOUNDING_BOX);
1523     }
1524     gfxdev->endString(state);
1525 }
1526 void BitmapOutputDev::endStringOp(GfxState *state)
1527 {
1528     msg("<debug> endStringOp");
1529     clip0dev->endStringOp(state);
1530     clip1dev->endStringOp(state);
1531     booltextdev->endStringOp(state);
1532     gfxdev->endStringOp(state);
1533 }
1534
1535 /* TODO: these four operations below *should* do nothing, as type3
1536          chars are drawn using operations like fill() */
1537 GBool BitmapOutputDev::beginType3Char(GfxState *state, double x, double y,
1538                              double dx, double dy,
1539                              CharCode code, Unicode *u, int uLen)
1540 {
1541     msg("<debug> beginType3Char");
1542     /* call gfxdev so that it can generate "invisible" characters
1543        on top of the actual graphic content, for text extraction */
1544     return gfxdev->beginType3Char(state, x, y, dx, dy, code, u, uLen);
1545 }
1546 void BitmapOutputDev::type3D0(GfxState *state, double wx, double wy)
1547 {
1548     msg("<debug> type3D0");
1549     return gfxdev->type3D0(state, wx, wy);
1550 }
1551 void BitmapOutputDev::type3D1(GfxState *state, double wx, double wy, double llx, double lly, double urx, double ury)
1552 {
1553     msg("<debug> type3D1");
1554     return gfxdev->type3D1(state, wx, wy, llx, lly, urx, ury);
1555 }
1556 void BitmapOutputDev::endType3Char(GfxState *state)
1557 {
1558     msg("<debug> endType3Char");
1559     gfxdev->endType3Char(state);
1560 }
1561
1562 class CopyStream: public Object
1563 {
1564     Dict*dict;
1565     char*buf;
1566     MemStream*memstream;
1567     public:
1568     CopyStream(Stream*str, int len)
1569     {
1570         buf = 0;
1571         str->reset();
1572         if(len) {
1573             buf = (char*)malloc(len);
1574             int t;
1575             for (t=0; t<len; t++)
1576               buf[t] = str->getChar();
1577         }
1578         str->close();
1579         this->dict = str->getDict();
1580         this->memstream = new MemStream(buf, 0, len, this);
1581     }
1582     ~CopyStream() 
1583     {
1584         ::free(this->buf);this->buf = 0;
1585         delete this->memstream;
1586     }
1587     Dict* getDict() {return dict;}
1588     Stream* getStream() {return this->memstream;};
1589 };
1590
1591 gfxbbox_t BitmapOutputDev::getImageBBox(GfxState*state)
1592 {
1593     gfxbbox_t bbox;
1594     double x,y;
1595     state->transform(0, 1, &x, &y);
1596     bbox.xmin=bbox.xmax = x;
1597     bbox.ymin=bbox.ymax = x;
1598     state->transform(0, 0, &x, &y);
1599     bbox.xmin=min(bbox.xmin,x);
1600     bbox.ymin=min(bbox.ymin,y);
1601     bbox.xmax=max(bbox.xmin,x);
1602     bbox.ymax=max(bbox.ymin,y);
1603     state->transform(1, 0, &x, &y);
1604     bbox.xmin=min(bbox.xmin,x);
1605     bbox.ymin=min(bbox.ymin,y);
1606     bbox.xmax=max(bbox.xmin,x);
1607     bbox.ymax=max(bbox.ymin,y);
1608     state->transform(1, 1, &x, &y);
1609     bbox.xmin=min(bbox.xmin,x);
1610     bbox.ymin=min(bbox.ymin,y);
1611     bbox.xmax=max(bbox.xmin,x);
1612     bbox.ymax=max(bbox.ymin,y);
1613     return bbox;
1614 }
1615 void BitmapOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
1616                            int width, int height, GBool invert,
1617                            GBool inlineImg)
1618 {
1619     msg("<debug> drawImageMask streamkind=%d", str->getKind());
1620     CopyStream*cpystr = 0;
1621     if(inlineImg) {
1622         cpystr = new CopyStream(str, height * ((width + 7) / 8));
1623         str = cpystr->getStream();
1624     }
1625     boolpolydev->drawImageMask(state, ref, str, width, height, invert, inlineImg);
1626     gfxbbox_t bbox = getImageBBox(state);
1627     checkNewBitmap(bbox.xmin, bbox.ymin, ceil(bbox.xmax), ceil(bbox.ymax));
1628     rgbdev->drawImageMask(state, ref, str, width, height, invert, inlineImg);
1629     if(cpystr)
1630         delete cpystr;
1631 }
1632 void BitmapOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
1633                        int width, int height, GfxImageColorMap *colorMap,
1634                        int *maskColors, GBool inlineImg)
1635 {
1636     msg("<debug> drawImage streamkind=%d", str->getKind());
1637     CopyStream*cpystr = 0;
1638     if(inlineImg) {
1639         cpystr = new CopyStream(str, height * ((width * colorMap->getNumPixelComps() * colorMap->getBits() + 7) / 8));
1640         str = cpystr->getStream();
1641     }
1642     boolpolydev->drawImage(state, ref, str, width, height, colorMap, maskColors, inlineImg);
1643     gfxbbox_t bbox=getImageBBox(state);
1644     checkNewBitmap(bbox.xmin, bbox.ymin, ceil(bbox.xmax), ceil(bbox.ymax));
1645     rgbdev->drawImage(state, ref, str, width, height, colorMap, maskColors, inlineImg);
1646     if(cpystr)
1647         delete cpystr;
1648 }
1649 void BitmapOutputDev::drawMaskedImage(GfxState *state, Object *ref, Stream *str,
1650                              int width, int height,
1651                              GfxImageColorMap *colorMap,
1652                              Stream *maskStr, int maskWidth, int maskHeight,
1653                              GBool maskInvert)
1654 {
1655     msg("<debug> drawMaskedImage streamkind=%d", str->getKind());
1656     boolpolydev->drawMaskedImage(state, ref, str, width, height, colorMap, maskStr, maskWidth, maskHeight, maskInvert);
1657     gfxbbox_t bbox=getImageBBox(state);
1658     checkNewBitmap(bbox.xmin, bbox.ymin, ceil(bbox.xmax), ceil(bbox.ymax));
1659     rgbdev->drawMaskedImage(state, ref, str, width, height, colorMap, maskStr, maskWidth, maskHeight, maskInvert);
1660 }
1661 void BitmapOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str,
1662                                  int width, int height,
1663                                  GfxImageColorMap *colorMap,
1664                                  Stream *maskStr,
1665                                  int maskWidth, int maskHeight,
1666                                  GfxImageColorMap *maskColorMap)
1667 {
1668     msg("<debug> drawSoftMaskedImage %dx%d (%dx%d) streamkind=%d", width, height, maskWidth, maskHeight, str->getKind());
1669     boolpolydev->drawSoftMaskedImage(state, ref, str, width, height, colorMap, maskStr, maskWidth, maskHeight, maskColorMap);
1670     gfxbbox_t bbox=getImageBBox(state);
1671     checkNewBitmap(bbox.xmin, bbox.ymin, ceil(bbox.xmax), ceil(bbox.ymax));
1672     rgbdev->drawSoftMaskedImage(state, ref, str, width, height, colorMap, maskStr, maskWidth, maskHeight, maskColorMap);
1673 }
1674 void BitmapOutputDev::drawForm(Ref id)
1675 {
1676     msg("<debug> drawForm");
1677     boolpolydev->drawForm(id);
1678     checkNewBitmap(UNKNOWN_BOUNDING_BOX);
1679     rgbdev->drawForm(id);
1680 }
1681
1682 void BitmapOutputDev::processLink(Link *link, Catalog *catalog)
1683 {
1684     msg("<debug> processLink");
1685     gfxdev->processLink(link, catalog);
1686 }
1687
1688 void BitmapOutputDev::beginTransparencyGroup(GfxState *state, double *bbox,
1689                                     GfxColorSpace *blendingColorSpace,
1690                                     GBool isolated, GBool knockout,
1691                                     GBool forSoftMask)
1692 {
1693     msg("<debug> beginTransparencyGroup");
1694 #if (xpdfMajorVersion*10000 + xpdfMinorVersion*100 + xpdfUpdateVersion) < 30207
1695     GfxState*state1 = state->copy();
1696     GfxState*state2 = state->copy();
1697     state1->setPath(0);
1698     state1->setPath(state->getPath()->copy());
1699     state2->setPath(0);
1700     state2->setPath(state->getPath()->copy());
1701 #else
1702     GfxState*state1 = state->copy(gTrue);
1703     GfxState*state2 = state->copy(gTrue);
1704 #endif
1705     boolpolydev->beginTransparencyGroup(state1, bbox, blendingColorSpace, isolated, knockout, forSoftMask);
1706     rgbdev->beginTransparencyGroup(state2, bbox, blendingColorSpace, isolated, knockout, forSoftMask);
1707     clip1dev->beginTransparencyGroup(state, bbox, blendingColorSpace, isolated, knockout, forSoftMask);
1708     delete state1;
1709     delete state2;
1710 }
1711 void BitmapOutputDev::endTransparencyGroup(GfxState *state)
1712 {
1713     msg("<debug> endTransparencyGroup");
1714 #if (xpdfMajorVersion*10000 + xpdfMinorVersion*100 + xpdfUpdateVersion) < 30207
1715     GfxState*state1 = state->copy();
1716     GfxState*state2 = state->copy();
1717     state1->setPath(0);
1718     state1->setPath(state->getPath()->copy());
1719     state2->setPath(0);
1720     state2->setPath(state->getPath()->copy());
1721 #else
1722     GfxState*state1 = state->copy(gTrue);
1723     GfxState*state2 = state->copy(gTrue);
1724 #endif
1725     boolpolydev->endTransparencyGroup(state1);
1726     checkNewBitmap(UNKNOWN_BOUNDING_BOX);
1727     rgbdev->endTransparencyGroup(state2);
1728     delete state1;
1729     delete state2;
1730     clip1dev->endTransparencyGroup(state);
1731 }
1732 void BitmapOutputDev::paintTransparencyGroup(GfxState *state, double *bbox)
1733 {
1734     msg("<debug> paintTransparencyGroup");
1735     boolpolydev->paintTransparencyGroup(state,bbox);
1736     checkNewBitmap(UNKNOWN_BOUNDING_BOX);
1737     rgbdev->paintTransparencyGroup(state,bbox);
1738     clip1dev->paintTransparencyGroup(state,bbox);
1739 }
1740 void BitmapOutputDev::setSoftMask(GfxState *state, double *bbox, GBool alpha, Function *transferFunc, GfxColor *backdropColor)
1741 {
1742     msg("<debug> setSoftMask");
1743     boolpolydev->setSoftMask(state, bbox, alpha, transferFunc, backdropColor);
1744     checkNewBitmap(UNKNOWN_BOUNDING_BOX);
1745     rgbdev->setSoftMask(state, bbox, alpha, transferFunc, backdropColor);
1746     clip1dev->setSoftMask(state, bbox, alpha, transferFunc, backdropColor);
1747 }
1748 void BitmapOutputDev::clearSoftMask(GfxState *state)
1749 {
1750     msg("<debug> clearSoftMask");
1751     boolpolydev->clearSoftMask(state);
1752     checkNewBitmap(UNKNOWN_BOUNDING_BOX);
1753     rgbdev->clearSoftMask(state);
1754     clip1dev->clearSoftMask(state);
1755 }