+
+ this->emptypage = 0;
+}
+
+void BitmapOutputDev::flushText()
+{
+ msg("<verbose> Flushing text/polygons");
+ gfxdevice_record_flush(this->gfxoutput, this->dev);
+
+ this->emptypage = 0;
+}
+
+void writeBitmap(SplashBitmap*bitmap, char*filename)
+{
+ int y,x;
+
+ int width = bitmap->getWidth();
+ int height = bitmap->getHeight();
+
+ gfxcolor_t*data = (gfxcolor_t*)malloc(sizeof(gfxcolor_t)*width*height);
+
+ for(y=0;y<height;y++) {
+ gfxcolor_t*line = &data[y*width];
+ for(x=0;x<width;x++) {
+ Guchar c[4];
+ bitmap->getPixel(x,y,c);
+ int a = bitmap->getAlpha(x,y);
+ line[x].r = c[0];
+ line[x].g = c[1];
+ line[x].b = c[2];
+ line[x].a = a;
+ }
+ }
+ writePNG(filename, (unsigned char*)data, width, height);
+ free(data);
+}
+
+void writeAlpha(SplashBitmap*bitmap, char*filename)
+{
+ int y,x;
+
+ int width = bitmap->getWidth();
+ int height = bitmap->getHeight();
+
+ gfxcolor_t*data = (gfxcolor_t*)malloc(sizeof(gfxcolor_t)*width*height);
+
+ for(y=0;y<height;y++) {
+ gfxcolor_t*line = &data[y*width];
+ for(x=0;x<width;x++) {
+ int a = bitmap->getAlpha(x,y);
+ line[x].r = a;
+ line[x].g = a;
+ line[x].b = a;
+ line[x].a = 255;
+ }
+ }
+ writePNG(filename, (unsigned char*)data, width, height);
+ free(data);
+}
+static int dbg_btm_counter=1;
+
+static const char*STATE_NAME[] = {"parallel", "textabovebitmap", "bitmapabovetext"};
+
+int checkAlphaSanity(SplashBitmap*boolbtm, SplashBitmap*alphabtm)
+{
+ assert(boolbtm->getWidth() == alphabtm->getWidth());
+ assert(boolbtm->getHeight() == alphabtm->getHeight());
+ if(boolbtm->getMode()==splashModeMono1) {
+ return 1;
+ }
+
+ int width = boolbtm->getWidth();
+ int height = boolbtm->getHeight();
+
+ int bad=0;
+ int x,y;
+ for(y=0;y<height;y++) {
+ for(x=0;x<width;x++) {
+ int a1 = alphabtm->getAlpha(x,y);
+ int a2 = boolbtm->getAlpha(x,y);
+ if(a1!=a2) {
+ bad++;
+ }
+ }
+ }
+ double badness = bad/(double)(width*height);
+ if(badness>0.2) {
+ msg("<error> Bitmaps don't correspond: %d out of %d pixels wrong (%.2f%%)", bad, width*height,
+ badness*100.0);
+ return 0;
+ }
+ msg("<notice> %f", badness);
+ return 1;
+}
+
+GBool BitmapOutputDev::checkNewText(int x1, int y1, int x2, int y2)
+{
+ /* called once some new text was drawn on booltextdev, and
+ before the same thing is drawn on gfxdev */
+
+ msg("<trace> Testing new text data against current bitmap data, state=%s, counter=%d\n", STATE_NAME[layerstate], dbg_btm_counter);
+
+ char filename1[80];
+ char filename2[80];
+ char filename3[80];
+ sprintf(filename1, "state%dboolbitmap_afternewtext.png", dbg_btm_counter);
+ sprintf(filename2, "state%dbooltext_afternewtext.png", dbg_btm_counter);
+ sprintf(filename3, "state%dbitmap_afternewtext.png", dbg_btm_counter);
+ if(0) {
+ msg("<verbose> %s %s %s", filename1, filename2, filename3);
+ writeAlpha(boolpolybitmap, filename1);
+ writeAlpha(booltextbitmap, filename2);
+ writeBitmap(rgbdev->getBitmap(), filename3);
+ }
+ dbg_btm_counter++;
+
+ GBool ret = false;
+ if(intersection(x1,y1,x2,y2)) {
+ if(layerstate==STATE_PARALLEL) {
+ /* the new text is above the bitmap. So record that fact,
+ and also clear the bitmap buffer, so we can check for
+ new intersections */
+ msg("<verbose> Text is above current bitmap/polygon data");
+ layerstate=STATE_TEXT_IS_ABOVE;
+ clearBoolPolyDev(x1,y1,x2,y2);
+ } else if(layerstate==STATE_BITMAP_IS_ABOVE) {
+ /* there's a bitmap above the (old) text. So we need
+ to flush out that text, and record that the *new*
+ text is now *above* the bitmap
+ */
+ msg("<verbose> Text is above current bitmap/polygon data (which is above some other text)");
+ flushText();
+ layerstate=STATE_TEXT_IS_ABOVE;
+ /* clear both bool devices- the text device because
+ we just dumped out all the (old) text, and the
+ poly dev so we can check for new intersections */
+ clearBoolPolyDev(x1,y1,x2,y2);
+ /* FIXME this destroys the text pixels we just
+ drew to test for the intersection- however we need
+ those to check for the *new* intersections */
+ clearBoolTextDev(x1,y1,x2,y2);
+ ret = true;
+ } else {
+ /* we already know that the current text section is
+ above the current bitmap section- now just new
+ bitmap data *and* new text data was drawn, and
+ *again* it's above the current bitmap- so clear
+ the polygon bitmap again, so we can check for
+ new intersections */
+ msg("<verbose> Text is still above current bitmap/polygon data");
+ clearBoolPolyDev(x1,y1,x2,y2);
+ }
+ }
+ return ret;
+}
+
+GBool BitmapOutputDev::checkNewBitmap(int x1, int y1, int x2, int y2)
+{
+ /* similar to checkNewText() above, only in reverse */
+ msg("<trace> Testing new graphics data against current text data, state=%s, counter=%d\n", STATE_NAME[layerstate], dbg_btm_counter);
+
+ char filename1[80];
+ char filename2[80];
+ char filename3[80];
+ sprintf(filename1, "state%dboolbitmap_afternewgfx.png", dbg_btm_counter);
+ sprintf(filename2, "state%dbooltext_afternewgfx.png", dbg_btm_counter);
+ sprintf(filename3, "state%dbitmap_afternewgfx.png", dbg_btm_counter);
+
+ if(0) {
+ msg("<verbose> %s %s %s", filename1, filename2, filename3);
+ writeAlpha(boolpolybitmap, filename1);
+ writeAlpha(booltextbitmap, filename2);
+ writeBitmap(rgbdev->getBitmap(), filename3);
+ }
+ dbg_btm_counter++;
+
+ GBool ret = false;
+ if(intersection(x1,y1,x2,y2)) {
+ if(layerstate==STATE_PARALLEL) {
+ msg("<verbose> Bitmap is above current text data");
+ layerstate=STATE_BITMAP_IS_ABOVE;
+ clearBoolTextDev(x1,y1,x2,y2);
+ } else if(layerstate==STATE_TEXT_IS_ABOVE) {
+ msg("<verbose> Bitmap is above current text data (which is above some bitmap)");
+ flushBitmap();
+ layerstate=STATE_BITMAP_IS_ABOVE;
+ clearBoolTextDev(x1,y1,x2,y2);
+ /* FIXME this destroys the polygon pixels we just
+ drew to test for the intersection- however we need
+ those to check for the *new* intersections */
+ clearBoolPolyDev(x1,y1,x2,y2);
+ ret = true;
+ } else {
+ msg("<verbose> Bitmap is still above current text data");
+ clearBoolTextDev(x1,y1,x2,y2);
+ }
+ }
+ return ret;
+}
+
+//void checkNewText() {
+// Guchar*alpha = rgbbitmap->getAlphaPtr();
+// Guchar*charpixels = clip1bitmap->getDataPtr();
+// int xx,yy;
+// for(yy=0;yy<height;yy++) {
+// Guchar*aline = &alpha[yy*width];
+// Guchar*cline = &charpixels[yy*width8];
+// for(xx=0;xx<width;xx++) {
+// int bit = xx&7;
+// int bytepos = xx>>3;
+// /* TODO: is the bit order correct? */
+// if(aline[xx] && (cline[bytepos]&(1<<bit)))
+// break;
+// }
+// if(xx!=width)
+// break;
+//}
+
+GBool BitmapOutputDev::clip0and1differ(int x1,int y1,int x2,int y2)
+{
+ if(clip0bitmap->getMode()==splashModeMono1) {
+ if(x2<=x1)
+ return gFalse;
+ if(x2<0)
+ return gFalse;
+ if(x1<0)
+ x1 = 0;
+ if(x1>=clip0bitmap->getWidth())
+ return gFalse;
+ if(x2>clip0bitmap->getWidth())
+ x2=clip0bitmap->getWidth();
+
+ if(y2<=y1)
+ return gFalse;
+ if(y2<0)
+ return gFalse;
+ if(y1<0)
+ y1 = 0;
+ if(y1>=clip0bitmap->getHeight())
+ return gFalse;
+ if(y2>clip0bitmap->getHeight())
+ y2=clip0bitmap->getHeight();
+
+ SplashBitmap*clip0 = clip0bitmap;
+ SplashBitmap*clip1 = clip1bitmap;
+ int width8 = (clip0bitmap->getWidth()+7)/8;
+ int height = clip0bitmap->getHeight();
+ int x18 = x1/8;
+ int x28 = (x2+7)/8;
+ int y;
+
+ for(y=y1;y<y2;y++) {
+ unsigned char*row1 = &clip0bitmap->getDataPtr()[width8*y+x18];
+ unsigned char*row2 = &clip1bitmap->getDataPtr()[width8*y+x28];
+ if(memcmp(row1, row2, x28-x18))
+ return gTrue;
+ }
+ return gFalse;
+ } else {
+ SplashBitmap*clip0 = clip0bitmap;
+ SplashBitmap*clip1 = clip1bitmap;
+ int width = clip0->getAlphaRowSize();
+ int height = clip0->getHeight();
+ return memcmp(clip0->getAlphaPtr(), clip1->getAlphaPtr(), width*height);
+ }
+}
+
+static void clearBooleanBitmap(SplashBitmap*btm, int x1, int y1, int x2, int y2)
+{
+ if(!(x1|y1|x2|y2)) {
+ x1 = y1 = 0;
+ x2 = btm->getWidth();
+ y2 = btm->getHeight();
+ }
+ if(btm->getMode()==splashModeMono1) {
+ int width8 = (btm->getWidth()+7)/8;
+ int width = btm->getWidth();
+ int height = btm->getHeight();
+ memset(btm->getDataPtr(), 0, width8*height);
+ } else {
+ int width = btm->getAlphaRowSize();
+ int height = btm->getHeight();
+ memset(btm->getAlphaPtr(), 0, width*height);
+ }