← Back to team overview

kicad-developers team mailing list archive

Re: [PATCH] Option to subtract soldermask from silkscreen in gerber output

 

On 12/12/2010 01:57 PM, Lorenzo Marcantonio wrote:
> On Sun, 12 Dec 2010, Dick Hollenbeck wrote:
>
>> The problem with ORing the colors is that as you get more layers, eventually the
>> intersections all turn white, no?
>> (Ok, on a normal board, we don't have so many mutliple gerber layer intersections,
>> true.  But if it can go wrong it eventually will.)
> A couple of supply layers and two signal parallel or crossing is actually pretty
> normal (in fact some stackups uses power layers to decouple parallel
> running traces on different layers).
>
> I think it will happen pretty soon:D


I agree.  Attached is a gerbview of a board using attached [PATCH] that uses blit with
mask.  It is essentially Marco's original code, but changed to buffer the destination
off screen, all the way up until the final blit to screen.

It looks very very nice, and I still the see the need to have this mode, as well as
the transparent or ORing mode both.


But I am unable to get any kind of performance on Linux when using the "mask with
GR_COPY" form of blit.  The mask seems to bring everything to a halt.

I originally thought that the entire bottleneck was going to screen memory, but that
has shown not to be the case.

After about 6 layers loaded, the performance comes to a crawl.

So I attach my 1/2 day's work as a patch, and give up.  The algorithm may come in
handy someday once we have a better graphic platform.


I am "crying uncle".  Note, this is not the same thing as "Bob is your uncle".  Bob is
not your uncle, tonight.


Jean-Pierre, you might want to take an hour look at this patch, but know that on Linux
it is no where near being usable.  I don't know where all the speed is going, we now
have all memory resident bitmaps, and still with mask the performance comes to a
crawl.  The ORing code might be usable, but have you tested that with several layers
loaded?  If also slow, we may have to revert to PRE- Marco's gerbview patch.

It is in your capable hands now, Jean-Pierre.  I am not thrilled by our graphics
environment.  We have our graphics API which is a wrapper over wxDC, which is a
wrapper over something else that is OS dependent.  It is basically a mess in my
opinion.  Torsten's code should be considered and extensively evalutated.  We owe him
that.  If is close to useable, we can obviously modify it.

It is past time to find a better graphics environment, IMO.

Other problems with this patch, or TO DO items if you prefer:

1) It uses bitmaps even for the pageprint(), which probably breaks SVG.  Same for what
is now in the repo.

2) I had to temporarily move the DrawBackground() into BOARD::Draw() which is not a
general solution, since it breaks printing.  I wanted to move my wxMemoryDC for the
screen up into RedrawActiveWindow() but doing that with the broken blit support, which
you say does not support scaling, was too much work for this simple experimentation. 
I never saw any scaling bugs, so I made your SetUserScale() calls conditional on a define.


Dick

Attachment: gerber-blit-with-mask.png
Description: PNG image

--- /svn/kicad/testing.checkout/gerbview/draw_gerber_screen.cpp	2010-12-12 11:51:54.000000000 -0600
+++ gerbview/draw_gerber_screen.cpp	2010-12-12 22:20:50.000000000 -0600
@@ -18,6 +18,11 @@
 #include "class_gerber_draw_item.h"
 #include "class_GERBER.h"
 
+
+#define AVOID_BLIT_SCALE_BUG    false   // not needed on Linux
+
+
+
 static void Show_Items_DCode_Value( WinEDA_DrawPanel* panel, wxDC* DC,
                                     BOARD* Pcb, int drawmode );
 
@@ -73,12 +78,14 @@
 
     if( !GetBoard() )
         return;
+
     ActiveScreen = screen;
 
     GRSetDrawMode( DC, GR_COPY );
-    DrawPanel->DrawBackGround( DC );
 
-    GetBoard()->Draw( DrawPanel, DC, GR_COPY, wxPoint( 0, 0 ) );
+    GetBoard()->Draw( DrawPanel, DC,
+                GR_OR,                  // this needs to be GR_COPY or GR_OR, set from a toggle button.
+                wxPoint( 0, 0 ) );
 
     if( IsElementVisible( DCODES_VISIBLE ) )
         Show_Items_DCode_Value( DrawPanel, DC, GetBoard(), GR_COPY );
@@ -99,34 +106,47 @@
 /********************************************************************/
 void BOARD::Draw( WinEDA_DrawPanel* aPanel, wxDC* aDC, int aDrawMode, const wxPoint& aOffset )
 /********************************************************************/
-/* Redraw the BOARD items but not cursors, axis or grid */
 {
     // Because Images can be negative (i.e with background filled in color) items are drawn
     // graphic layer per graphic layer, after the background is filled
-    int        bitmapWidth, bitmapHeight;
 
+    wxColour    bgColor = MakeColour( g_DrawBgColor );
+    wxBrush     bgBrush( bgColor, wxSOLID );
+
+    int         bitmapWidth, bitmapHeight;
+
+#if AVOID_BLIT_SCALE_BUG
     // Store device context scale and origins:
-    double dc_scalex, dc_scaley;
-    wxPoint dev_org;
-    wxPoint logical_org;
+    double      dc_scalex, dc_scaley;
+    wxPoint     dev_org;
+    wxPoint     logical_org;
+
     aDC->GetDeviceOrigin( &dev_org.x, &dev_org.y );
     aDC->GetLogicalOrigin( &logical_org.x, &logical_org.y );
-    aDC->GetUserScale( &dc_scalex, &dc_scaley);
+    aDC->GetUserScale( &dc_scalex, &dc_scaley );
 
-    // Blit function used below seems work OK only with scale = 1 and no offsets
+    // Blit function used below seems to work OK only with scale = 1 and no offsets
     aDC->SetUserScale( 1.0, 1.0 );
-    aDC->SetDeviceOrigin( 0,0 );
-    aDC->SetLogicalOrigin( 0,0 );
+    aDC->SetDeviceOrigin( 0, 0 );
+    aDC->SetLogicalOrigin( 0, 0 );
+#endif
 
     aPanel->GetClientSize( &bitmapWidth, &bitmapHeight );
 
-    wxBitmap   layerBitmap( bitmapWidth, bitmapHeight );
+    wxBitmap    layerBitmap( bitmapWidth, bitmapHeight );
+    wxBitmap    screenBitmap( bitmapWidth, bitmapHeight );
+
+    wxMemoryDC  layerDC;        // used sequentially for each gerber layer
+    wxMemoryDC  screenDC;
 
-    wxMemoryDC memoryDC;
-    memoryDC.SelectObject( layerBitmap );
+    layerDC.SelectObject(  layerBitmap );
+    screenDC.SelectObject( screenBitmap );
+
+    screenDC.SetBackground( bgBrush );
+    screenDC.Clear();
+
+    aPanel->DrawBackGround( &screenDC );
 
-    wxColour   bgColor = MakeColour( g_DrawBgColor );
-    wxBrush    bgBrush( bgColor, wxSOLID );
     for( int layer = 0; layer < 32; layer++ )
     {
         if( !GetBoard()->IsLayerVisible( layer ) )
@@ -136,29 +156,31 @@
         if( gerber == NULL )    // Graphic layer not yet used
             continue;
 
-        memoryDC.SetUserScale( dc_scalex, dc_scaley );
-        memoryDC.SetDeviceOrigin( dev_org.x, dev_org.y );
-        memoryDC.SetLogicalOrigin( logical_org.x, logical_org.y );
+#if AVOID_BLIT_SCALE_BUG
+        layerDC.SetUserScale( dc_scalex, dc_scaley );
+        layerDC.SetDeviceOrigin( dev_org.x, dev_org.y );
+        layerDC.SetLogicalOrigin( logical_org.x, logical_org.y );
+#endif
+
         // Draw each layer into a bitmap first. Negative Gerber
         // layers are drawn in background color.
-        memoryDC.SetBackground( bgBrush );
-        memoryDC.Clear();
+        layerDC.SetBackground( bgBrush );
+        layerDC.Clear();
 
         if( gerber->m_ImageNegative )
         {
             // Draw background negative (i.e. in graphic layer color) for negative images.
-
             int color = GetBoard()->GetLayerColor( layer );
 
-            GRSetDrawMode( (wxDC*)&memoryDC, GR_COPY ); // GR_COPY is faster than GR_OR
+            GRSetDrawMode( &layerDC, GR_COPY );
 
             EDA_Rect* cbox = &aPanel->m_ClipBox;
 
-            GRSFilledRect( cbox, (wxDC*)&memoryDC, cbox->GetX(), cbox->GetY(),
+            GRSFilledRect( cbox, &layerDC, cbox->GetX(), cbox->GetY(),
                            cbox->GetRight(), cbox->GetBottom(),
                            0, color, color );
 
-            GRSetDrawMode( (wxDC*)&memoryDC, aDrawMode );
+            GRSetDrawMode( &layerDC, GR_COPY );
         }
 
         int dcode_highlight = 0;
@@ -171,42 +193,54 @@
             if( gerb_item->GetLayer() != layer )
                 continue;
 
-            int drawMode = aDrawMode;
+            int drawMode = GR_COPY;     // after all, the layer has its own DC
             if( dcode_highlight == gerb_item->m_DCode )
                 drawMode |= GR_SURBRILL;
 
-            gerb_item->Draw( aPanel, (wxDC*)&memoryDC, drawMode );
+            gerb_item->Draw( aPanel, &layerDC, drawMode );
         }
 
-#if 0
-        // Use the layer bitmap itself as a mask when blitting.
-        // The bitmap cannot be referenced by a device context
-        // when setting the mask.
-        memoryDC.SelectObject( wxNullBitmap );
-        layerBitmap.SetMask( new wxMask( layerBitmap, bgColor ) );
-
-        memoryDC.SelectObject( layerBitmap );
-
-        aDC->Blit( 0, 0, bitmapWidth, bitmapHeight,
-                   (wxDC*)&memoryDC, 0, 0, wxCOPY, true );
-
-#else   // Dick: seems a little faster, crisper
-
-        // Blit function seems work OK only with scale = 1 and no offsets
-        memoryDC.SetUserScale( 1.0, 1.0 );
-        memoryDC.SetDeviceOrigin( 0,0 );
-        memoryDC.SetLogicalOrigin( 0,0 );
-        aDC->Blit( 0, 0, bitmapWidth, bitmapHeight,
-                   &memoryDC, 0, 0, wxOR, false );
-
+#if AVOID_BLIT_SCALE_BUG
+        // Blit function seems to work OK only with scale = 1 and no offsets
+        layerDC.SetUserScale( 1.0, 1.0 );
+        layerDC.SetDeviceOrigin( 0, 0 );
+        layerDC.SetLogicalOrigin( 0, 0 );
 #endif
 
+        if( aDrawMode == GR_COPY )
+        {
+            // Use the layer bitmap itself as a mask when blitting.
+            // The bitmap cannot be referenced by a device context
+            // when setting the mask.
+            layerDC.SelectObject( wxNullBitmap );
+            layerBitmap.SetMask( new wxMask( layerBitmap, bgColor ) );
+
+            layerDC.SelectObject( layerBitmap );
+
+            screenDC.Blit( 0, 0, bitmapWidth, bitmapHeight, &layerDC, 0, 0, wxCOPY, true );
+        }
+
+        else if( aDrawMode == GR_OR )
+        {
+            // On Linux with a large screen, this version is much faster and without flicker,
+            // but gives a PCBNEW look where layer colors blend together.  Plus it works
+            // only because the background color is black.  But it may be more useable for some.
+            // The difference is due in part because of the cpu cycles needed to create the
+            // monochromatic bitmap above, and the extra time needed to do bit indexing
+            // into the monochromatic bitmap on the blit above.
+            screenDC.Blit( 0, 0, bitmapWidth, bitmapHeight,
+                       &layerDC, 0, 0, wxOR, false );
+        }
     }
 
+    aDC->Blit( 0, 0, bitmapWidth, bitmapHeight, &screenDC, 0, 0, wxCOPY );
+
+#if AVOID_BLIT_SCALE_BUG
     // Restore scale and offsets values:
     aDC->SetUserScale( dc_scalex, dc_scaley );
     aDC->SetDeviceOrigin( dev_org.x, dev_org.y );
     aDC->SetLogicalOrigin( logical_org.x, logical_org.y );
+#endif
 
     m_PcbFrame->GetScreen()->ClrRefreshReq();
 }

References