From 3d73649bf0e39778e715a07da902d0a858065a43 Mon Sep 17 00:00:00 2001 From: Matthias Kramm Date: Tue, 2 Mar 2010 10:12:51 -0800 Subject: [PATCH] fixed z-order problems in poly2bitmap --- lib/pdf/BitmapOutputDev.cc | 253 ++++++++++++++++++++++++++------------------ lib/pdf/BitmapOutputDev.h | 8 +- 2 files changed, 153 insertions(+), 108 deletions(-) diff --git a/lib/pdf/BitmapOutputDev.cc b/lib/pdf/BitmapOutputDev.cc index 1a735e3..bfafe9d 100644 --- a/lib/pdf/BitmapOutputDev.cc +++ b/lib/pdf/BitmapOutputDev.cc @@ -103,6 +103,12 @@ BitmapOutputDev::~BitmapOutputDev() if(this->boolpolydev) { delete this->boolpolydev;this->boolpolydev = 0; } + if(this->stalepolybitmap) { + delete this->stalepolybitmap;this->stalepolybitmap = 0; + } + if(this->staletextbitmap) { + delete this->staletextbitmap;this->staletextbitmap = 0; + } if(this->booltextdev) { delete this->booltextdev;this->booltextdev = 0; } @@ -160,6 +166,9 @@ void BitmapOutputDev::setPageMap(int*page2page, int num_pages) } void writeBitmap(SplashBitmap*bitmap, char*filename); +void writeAlpha(SplashBitmap*bitmap, char*filename); + +static int dbg_btm_counter=1; void BitmapOutputDev::flushBitmap() { @@ -176,16 +185,28 @@ void BitmapOutputDev::flushBitmap() writeBitmap(rgbdev->getBitmap(), "test.png"); } counter++;*/ + /*static int counter=0; + char filename[160]; + sprintf(filename, "test%d.png", counter++); + writeBitmap(rgbbitmap, filename);*/ + SplashColorPtr rgb = rgbbitmap->getDataPtr(); Guchar*alpha = rgbbitmap->getAlphaPtr(); - Guchar*alpha2 = boolpolybitmap->getDataPtr(); - int width8 = (boolpolybitmap->getWidth()+7)/8; + Guchar*alpha2 = stalepolybitmap->getDataPtr(); + int width8 = (stalepolybitmap->getWidth()+7)/8; + + /*char filename[80]; + sprintf(filename, "flush%d_mask.png", dbg_btm_counter); + writeAlpha(stalepolybitmap, filename); + sprintf(filename, "flush%d_alpha.png", dbg_btm_counter); + writeAlpha(rgbbitmap, filename); + sprintf(filename, "flush%d_bitmap.png", dbg_btm_counter); + writeBitmap(rgbbitmap, filename);*/ ibbox_t* boxes = get_bitmap_bboxes((unsigned char*)alpha, width, height); ibbox_t*b; - for(b=boxes;b;b=b->next) { int xmin = b->xmin; int ymin = b->ymin; @@ -274,7 +295,7 @@ void BitmapOutputDev::flushBitmap() void BitmapOutputDev::flushText() { - msg(" Flushing text/polygons"); + msg(" Flushing text"); gfxdevice_record_flush(this->gfxoutput, this->dev); this->emptypage = 0; @@ -293,11 +314,11 @@ void writeMonoBitmap(SplashBitmap*btm, char*filename) gfxcolor_t*d = &b[width*y]; for(x=0;x>3]&(128>>(x&7))) { - d[x].r = d[x].g = d[x].b = 255; + d[x].r = d[x].b = d[x].a = 255; + d[x].g = 0; } else { - d[x].r = d[x].g = d[x].b = 0; + d[x].r = d[x].g = d[x].b = d[x].a = 0; } - d[x].a = 255; } } writePNG(filename, (unsigned char*)b, width, height); @@ -352,15 +373,14 @@ void writeAlpha(SplashBitmap*bitmap, char*filename) for(x=0;xgetAlpha(x,y); line[x].r = a; - line[x].g = a; + line[x].g = 0; line[x].b = a; - line[x].a = 255; + line[x].a = a; } } writePNG(filename, (unsigned char*)data, width, height); free(data); } -static int dbg_btm_counter=1; static const char*STATE_NAME[] = {"parallel", "textabovebitmap", "bitmapabovetext"}; @@ -396,6 +416,76 @@ int checkAlphaSanity(SplashBitmap*boolbtm, SplashBitmap*alphabtm) return 1; } +static inline GBool fixBBox(int*x1, int*y1, int*x2, int*y2, int width, int height) +{ + if(!(*x1|*y1|*x2|*y2)) { + // undefined bbox + *x1 = *y1 = 0; + *x2 = width; + *y2 = height; + return gTrue; + } + if(*x2<=*x1) return gFalse; + if(*x2<0) return gFalse; + if(*x1<0) *x1 = 0; + if(*x1>=width) return gFalse; + if(*x2>width) *x2=width; + + if(*y2<=*y1) return gFalse; + if(*y2<0) return gFalse; + if(*y1<0) *y1 = 0; + if(*y1>=height) return gFalse; + if(*y2>height) *y2=height; + return gTrue; +} + +static void update_bitmap(SplashBitmap*bitmap, SplashBitmap*update, int x1, int y1, int x2, int y2, char overwrite) +{ + assert(bitmap->getMode()==splashModeMono1); + assert(update->getMode()==splashModeMono1); + + int width8 = (bitmap->getWidth()+7)/8; + assert(width8 == bitmap->getRowSize()); + assert(width8 == update->getRowSize()); + int height = bitmap->getHeight(); + assert(height == update->getHeight()); + + if(!fixBBox(&x1, &y1, &x2, &y2, bitmap->getWidth(), bitmap->getHeight())) + return; + + Guchar*b = bitmap->getDataPtr() + y1*width8; + Guchar*u = update->getDataPtr() + y1*width8; + int size = (y2-y1)*width8; + + if(overwrite) { + memcpy(b, u, size); + } else { + int t; + for(t=0;tgetWidth(), btm->getHeight())) + return; + + if(btm->getMode()==splashModeMono1) { + int width8 = (btm->getWidth()+7)/8; + assert(width8 == btm->getRowSize()); + int width = btm->getWidth(); + int height = btm->getHeight(); + Guchar*data = btm->getDataPtr(); + memset(data+y1*width8, 0, width8*(y2-y1)); + } else { + int width = btm->getAlphaRowSize(); + int height = btm->getHeight(); + memset(btm->getAlphaPtr(), 0, width*height); + } +} + GBool BitmapOutputDev::checkNewText(int x1, int y1, int x2, int y2) { /* called once some new text was drawn on booltextdev, and @@ -411,21 +501,19 @@ GBool BitmapOutputDev::checkNewText(int x1, int y1, int x2, int y2) sprintf(filename2, "state%dbooltext_afternewtext.png", dbg_btm_counter); sprintf(filename3, "state%dbitmap_afternewtext.png", dbg_btm_counter); msg(" %s %s %s", filename1, filename2, filename3); - writeAlpha(boolpolybitmap, filename1); + writeAlpha(stalepolybitmap, filename1); writeAlpha(booltextbitmap, filename2); writeBitmap(rgbdev->getBitmap(), filename3); } dbg_btm_counter++; - + GBool ret = false; - if(intersection(x1,y1,x2,y2)) { + if(intersection(booltextbitmap, stalepolybitmap, 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 */ + /* the new text is above the bitmap. So record that fact. */ msg(" Text is above current bitmap/polygon data"); layerstate=STATE_TEXT_IS_ABOVE; - clearBoolPolyDev(x1,y1,x2,y2); + update_bitmap(staletextbitmap, booltextbitmap, x1, y1, x2, y2, 0); } 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* @@ -434,29 +522,28 @@ GBool BitmapOutputDev::checkNewText(int x1, int y1, int x2, int y2) msg(" 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); + + clearBoolTextDev(); + /* re-apply the update (which we would otherwise lose) */ + update_bitmap(staletextbitmap, booltextbitmap, x1, y1, x2, y2, 1); 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 */ + *again* it's above the current bitmap. */ msg(" Text is still above current bitmap/polygon data"); - clearBoolPolyDev(x1,y1,x2,y2); + update_bitmap(staletextbitmap, booltextbitmap, x1, y1, x2, y2, 0); } - } + } else { + update_bitmap(staletextbitmap, booltextbitmap, x1, y1, x2, y2, 0); + } + + /* clear the thing we just drew from our temporary drawing bitmap */ + clearBooleanBitmap(booltextbitmap, x1, y1, x2, y2); + return ret; } - GBool BitmapOutputDev::checkNewBitmap(int x1, int y1, int x2, int y2) { /* similar to checkNewText() above, only in reverse */ @@ -470,33 +557,36 @@ GBool BitmapOutputDev::checkNewBitmap(int x1, int y1, int x2, int y2) sprintf(filename2, "state%dbooltext_afternewgfx.png", dbg_btm_counter); sprintf(filename3, "state%dbitmap_afternewgfx.png", dbg_btm_counter); msg(" %s %s %s", filename1, filename2, filename3); - writeAlpha(boolpolybitmap, filename1); + writeAlpha(stalepolybitmap, filename1); writeAlpha(booltextbitmap, filename2); writeBitmap(rgbdev->getBitmap(), filename3); } dbg_btm_counter++; GBool ret = false; - if(intersection(x1,y1,x2,y2)) { + if(intersection(boolpolybitmap, staletextbitmap, x1,y1,x2,y2)) { if(layerstate==STATE_PARALLEL) { msg(" Bitmap is above current text data"); layerstate=STATE_BITMAP_IS_ABOVE; - clearBoolTextDev(x1,y1,x2,y2); + update_bitmap(stalepolybitmap, boolpolybitmap, x1, y1, x2, y2, 0); } else if(layerstate==STATE_TEXT_IS_ABOVE) { msg(" 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); + clearBoolPolyDev(); + update_bitmap(stalepolybitmap, boolpolybitmap, x1, y1, x2, y2, 1); ret = true; } else { msg(" Bitmap is still above current text data"); - clearBoolTextDev(x1,y1,x2,y2); + update_bitmap(stalepolybitmap, boolpolybitmap, x1, y1, x2, y2, 0); } - } + } else { + update_bitmap(stalepolybitmap, boolpolybitmap, x1, y1, x2, y2, 0); + } + + /* clear the thing we just drew from our temporary drawing bitmap */ + clearBooleanBitmap(boolpolybitmap, x1, y1, x2, y2); + return ret; } @@ -518,29 +608,6 @@ GBool BitmapOutputDev::checkNewBitmap(int x1, int y1, int x2, int y2) // break; //} -static inline GBool fixBBox(int*x1, int*y1, int*x2, int*y2, int width, int height) -{ - if(!(*x1|*y1|*x2|*y2)) { - // undefined bbox - *x1 = *y1 = 0; - *x2 = width; - *y2 = height; - return gTrue; - } - if(*x2<=*x1) return gFalse; - if(*x2<0) return gFalse; - if(*x1<0) *x1 = 0; - if(*x1>=width) return gFalse; - if(*x2>width) *x2=width; - - if(*y2<=*y1) return gFalse; - if(*y2<0) return gFalse; - if(*y1<0) *y1 = 0; - if(*y1>=height) return gFalse; - if(*y2>height) *y2=height; - return gTrue; -} - GBool BitmapOutputDev::clip0and1differ(int x1,int y1,int x2,int y2) { if(clip0bitmap->getMode()==splashModeMono1) { @@ -602,25 +669,6 @@ GBool BitmapOutputDev::clip0and1differ(int x1,int y1,int x2,int y2) } } -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); - } -} - GBool compare8(unsigned char*data1, unsigned char*data2, int len) { if(!len) @@ -659,11 +707,8 @@ GBool compare8(unsigned char*data1, unsigned char*data2, int len) return 0; } -GBool BitmapOutputDev::intersection(int x1, int y1, int x2, int y2) +GBool BitmapOutputDev::intersection(SplashBitmap*boolpoly, SplashBitmap*booltext, int x1, int y1, int x2, int y2) { - SplashBitmap*boolpoly = boolpolybitmap; - SplashBitmap*booltext = booltextbitmap; - if(boolpoly->getMode()==splashModeMono1) { /* alternative implementation, using one bit per pixel- needs the no-dither patch in xpdf */ @@ -750,7 +795,6 @@ GBool BitmapOutputDev::intersection(int x1, int y1, int x2, int y2) msg(" Bad bounding box: intersection outside bbox"); msg(" given bbox: %d %d %d %d", x1, y1, x2, y2); msg(" changed area: %d %d %d %d", ax1, ay1, ax2, ay2); - //writeAlpha(booltextbitmap, "alpha.png"); } return overlap2; } @@ -786,7 +830,13 @@ void BitmapOutputDev::startPage(int pageNum, GfxState *state, double crop_x1, do gfxdev->startPage(pageNum, state, crop_x1, crop_y1, crop_x2, crop_y2); boolpolybitmap = boolpolydev->getBitmap(); + stalepolybitmap = new SplashBitmap(boolpolybitmap->getWidth(), boolpolybitmap->getHeight(), 1, boolpolybitmap->getMode(), 0); + assert(stalepolybitmap->getRowSize() == boolpolybitmap->getRowSize()); + booltextbitmap = booltextdev->getBitmap(); + staletextbitmap = new SplashBitmap(booltextbitmap->getWidth(), booltextbitmap->getHeight(), 1, booltextbitmap->getMode(), 0); + assert(staletextbitmap->getRowSize() == booltextbitmap->getRowSize()); + clip0bitmap = clip0dev->getBitmap(); clip1bitmap = clip1dev->getBitmap(); rgbbitmap = rgbdev->getBitmap(); @@ -795,8 +845,8 @@ void BitmapOutputDev::startPage(int pageNum, GfxState *state, double crop_x1, do /* just in case any device did draw a white background rectangle into the device */ - clearBoolTextDev(UNKNOWN_BOUNDING_BOX); - clearBoolPolyDev(UNKNOWN_BOUNDING_BOX); + clearBoolTextDev(); + clearBoolPolyDev(); this->layerstate = STATE_PARALLEL; this->emptypage = 1; @@ -1251,9 +1301,7 @@ void BitmapOutputDev::fill(GfxState *state) msg(" fill"); boolpolydev->fill(state); gfxbbox_t bbox = getBBox(state); - if(checkNewBitmap(bbox.xmin, bbox.ymin, ceil(bbox.xmax), ceil(bbox.ymax))) { - boolpolydev->fill(state); - } + checkNewBitmap(bbox.xmin, bbox.ymin, ceil(bbox.xmax), ceil(bbox.ymax)); rgbdev->fill(state); } void BitmapOutputDev::eoFill(GfxState *state) @@ -1261,9 +1309,7 @@ void BitmapOutputDev::eoFill(GfxState *state) msg(" eoFill"); boolpolydev->eoFill(state); gfxbbox_t bbox = getBBox(state); - if(checkNewBitmap(bbox.xmin, bbox.ymin, ceil(bbox.xmax), ceil(bbox.ymax))) { - boolpolydev->eoFill(state); - } + checkNewBitmap(bbox.xmin, bbox.ymin, ceil(bbox.xmax), ceil(bbox.ymax)); rgbdev->eoFill(state); } #if (xpdfMajorVersion*10000 + xpdfMinorVersion*100 + xpdfUpdateVersion) < 30207 @@ -1364,13 +1410,13 @@ void BitmapOutputDev::clearClips() clearBooleanBitmap(clip0bitmap, 0, 0, 0, 0); clearBooleanBitmap(clip1bitmap, 0, 0, 0, 0); } -void BitmapOutputDev::clearBoolPolyDev(int x1, int y1, int x2, int y2) +void BitmapOutputDev::clearBoolPolyDev() { - clearBooleanBitmap(boolpolybitmap, x1, y1, x2, y2); + clearBooleanBitmap(stalepolybitmap, 0, 0, stalepolybitmap->getWidth(), stalepolybitmap->getHeight()); } -void BitmapOutputDev::clearBoolTextDev(int x1, int y1, int x2, int y2) +void BitmapOutputDev::clearBoolTextDev() { - clearBooleanBitmap(booltextbitmap, x1, y1, x2, y2); + clearBooleanBitmap(staletextbitmap, 0, 0, staletextbitmap->getWidth(), staletextbitmap->getHeight()); } void BitmapOutputDev::drawChar(GfxState *state, double x, double y, double dx, double dy, @@ -1388,7 +1434,6 @@ void BitmapOutputDev::drawChar(GfxState *state, double x, double y, } else if(rgbbitmap != rgbdev->getBitmap()) { // we're doing softmasking or transparency grouping boolpolydev->drawChar(state, x, y, dx, dy, originX, originY, code, nBytes, u, uLen); - //checkNewBitmap(UNKNOWN_BOUNDING_BOX); rgbdev->drawChar(state, x, y, dx, dy, originX, originY, code, nBytes, u, uLen); } else { // we're drawing a regular char @@ -1450,10 +1495,6 @@ void BitmapOutputDev::drawString(GfxState *state, GString *s) { msg(" internal error: drawString not implemented"); return; - clip0dev->drawString(state, s); - clip1dev->drawString(state, s); - booltextdev->drawString(state, s); - gfxdev->drawString(state, s); } void BitmapOutputDev::endTextObject(GfxState *state) { @@ -1474,6 +1515,8 @@ void BitmapOutputDev::endString(GfxState *state) booltextdev->endString(state); int render = state->getRender(); if(render != RENDER_INVISIBLE && render != RENDER_FILL) { + /* xpdf draws things like stroke text or fill+stroke text in the + endString() method */ checkNewText(UNKNOWN_BOUNDING_BOX); } gfxdev->endString(state); diff --git a/lib/pdf/BitmapOutputDev.h b/lib/pdf/BitmapOutputDev.h index 2c804b6..acf487b 100644 --- a/lib/pdf/BitmapOutputDev.h +++ b/lib/pdf/BitmapOutputDev.h @@ -187,14 +187,14 @@ public: private: void clearClips(); - void clearBoolPolyDev(int x1, int y1, int x2, int y2); - void clearBoolTextDev(int x1, int y1, int x2, int y2); + void clearBoolPolyDev(); + void clearBoolTextDev(); void flushText(); void flushBitmap(); GBool checkNewText(int x1, int y1, int x2, int y2); GBool checkNewBitmap(int x1, int y1, int x2, int y2); GBool clip0and1differ(int x1,int y1,int x2,int y2); - GBool intersection(int x1,int y1,int x2,int y2); + GBool intersection(SplashBitmap*boolpoly, SplashBitmap*booltext, int x1, int y1, int x2, int y2); virtual gfxbbox_t getImageBBox(GfxState*state); virtual gfxbbox_t getBBox(GfxState*state); @@ -218,7 +218,9 @@ private: SplashBitmap*clip0bitmap; SplashBitmap*clip1bitmap; SplashBitmap*boolpolybitmap; + SplashBitmap*stalepolybitmap; SplashBitmap*booltextbitmap; + SplashBitmap*staletextbitmap; gfxdevice_t* gfxoutput; GFXOutputDev*gfxdev; -- 1.7.10.4