//------------------------------------------------------------------------------ // Copyright (c) 2001-2002, Haiku, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a // copy of this software and associated documentation files (the "Software"), // to deal in the Software without restriction, including without limitation // the rights to use, copy, modify, merge, publish, distribute, sublicense, // and/or sell copies of the Software, and to permit persons to whom the // Software is furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. // // File Name: DisplayDriver.cpp // Author: DarkWyrm // Gabe Yoder // Description: Mostly abstract class which handles all graphics output // for the server // //------------------------------------------------------------------------------ #include #include "Angle.h" #include "FontFamily.h" #include #include "DisplayDriver.h" #include "RectUtils.h" #include "Utils.h" #include "ServerCursor.h" #include "CursorData.h" // TODO: Remove remnants of old API. Inplement all functions. Bounds checking needs to be // handled by the public drawing functions. // Add clipping and make sure public functions have Lock & Unlock. static Blitter blitter; /*! \brief Sets up internal variables needed by all DisplayDriver subclasses Subclasses should follow DisplayDriver's lead and use this function mostly for initializing data members. */ DisplayDriver::DisplayDriver(void) : fCursorHandler(this) { _locker=new BLocker(); fCursorHandler.SetCursor(new ServerCursor(default_cursor_data)); // _is_cursor_hidden=false; // _is_cursor_obscured=false; // _cursor=NULL; // _cursorsave=NULL; fDPMSCaps=B_DPMS_ON; fDPMSState=B_DPMS_ON; } /*! \brief Deletes the locking semaphore Subclasses should use the destructor mostly for freeing allocated heap space. */ DisplayDriver::~DisplayDriver(void) { delete _locker; } /*! \brief Initializes the driver object. \return true if successful, false if not Initialize sets up the driver for display, including the initial clearing of the screen. If things do not go as they should, false should be returned. */ bool DisplayDriver::Initialize(void) { return false; } /*! \brief Shuts down the driver's video subsystem Any work done by Initialize() should be undone here. Note that Shutdown() is called even if Initialize() was unsuccessful. */ void DisplayDriver::Shutdown(void) { } /*! \brief Called for all BView::CopyBits calls \param src Source rectangle. \param dest Destination rectangle. If the destination is not the same size as the source, the source should be scaled to fit. */ void DisplayDriver::CopyBits(const BRect &src, const BRect &dest, const DrawData *d) { if(!d) return; Lock(); if(fCursorHandler.IntersectsCursor(dest)) fCursorHandler.DriverHide(); Blit(src,dest,d); fCursorHandler.DriverShow(); Unlock(); } /*! \brief A screen-to-screen blit (of sorts) which copies a BRegion \param src Source region \param lefttop Offset to which the region will be copied */ void DisplayDriver::CopyRegion(BRegion *src, const BPoint &lefttop) { // TODO: Implement DisplayDriver;:CopyRegion } /*! \brief Called for all BView::DrawBitmap calls \param region Destination rects in screen coordinates \param bmp Bitmap to be drawn. It will always be non-NULL and valid. The color space is not guaranteed to match. \param src Source rectangle \param dest Destination rectangle. Source will be scaled to fit if not the same size. \param d Data structure containing any other data necessary for the call. Always non-NULL. */ void DisplayDriver::DrawBitmap(BRegion *region, ServerBitmap *bitmap, const BRect &source, const BRect &dest, const DrawData *d) { Lock(); FBBitmap frameBuffer; FBBitmap *bmp = &frameBuffer; blitter.Select(32, 32); if(!AcquireBuffer(&frameBuffer)) { debugger("ERROR: Couldn't acquire framebuffer in DrawBitmap()\n"); Unlock(); return; } if(fCursorHandler.IntersectsCursor(dest)) fCursorHandler.DriverHide(); uint8 colorspace_size = (bitmap->BitsPerPixel() + 7) / 8; int32 count = region->CountRects(); BRect bitmaprect(bitmap->Bounds()); BRect sourcerect(source); BRect destrect(dest); destrect.right *= d->scale; destrect.bottom *= d->scale; if(sourcerect.left < 0) sourcerect.left = 0; if(sourcerect.top < 0) sourcerect.top = 0; if(sourcerect.Width() > bitmaprect.Width()) sourcerect.right = bitmaprect.left + bitmaprect.Width(); if(sourcerect.Height() > bitmaprect.Height()) sourcerect.bottom = bitmaprect.top + bitmaprect.Height(); int32 sourcewidth = (int32)sourcerect.Width() + 1; int32 sourceheight = (int32)sourcerect.Height() + 1; int32 destwidth = (int32)destrect.Width() + 1; int32 destheight = (int32)destrect.Height() + 1; int32 xscale_factor = (sourcewidth << 16) / destwidth; int32 yscale_factor = (sourceheight << 16) / destheight; uint8 *src_bits = (uint8 *)bitmap->Bits(); uint8 *dest_bits = (uint8*)bmp->Bits(); int32 src_row = bitmap->BytesPerRow(); int32 dest_row = bmp->BytesPerRow(); src_bits += uint32((sourcerect.top * src_row) + (sourcerect.left * colorspace_size)); integer_rect src_integer_rectangle = BRect_to_integer_rect(sourcerect); integer_rect dst_integer_rectangle = BRect_to_integer_rect(destrect); int32 xscale_position = 0, yscale_position = 0, clipped_xscale_position = 0; for(int32 c = 0; c < count; c++) { integer_rect screen_integer_rect = BRect_to_integer_rect(region->RectAt(c)); integer_rect src_integer_rect = src_integer_rectangle; integer_rect dst_integer_rect = dst_integer_rectangle; xscale_position = 0, yscale_position = 0, clipped_xscale_position = 0; if(dst_integer_rect.x < screen_integer_rect.x) { dst_integer_rect.x -= screen_integer_rect.x; dst_integer_rect.w += dst_integer_rect.x; src_integer_rect.x -= dst_integer_rect.x; dst_integer_rect.x = screen_integer_rect.x; } if(dst_integer_rect.y < screen_integer_rect.y) { dst_integer_rect.y -= screen_integer_rect.y; dst_integer_rect.h += dst_integer_rect.y; src_integer_rect.y -= dst_integer_rect.y; dst_integer_rect.y = screen_integer_rect.y; } if(dst_integer_rect.w > screen_integer_rect.w) dst_integer_rect.w = screen_integer_rect.w; if(dst_integer_rect.h > screen_integer_rect.h) dst_integer_rect.h = screen_integer_rect.h; if(src_integer_rect.x > src_integer_rectangle.x) { int32 x_scale_pixel_multiply = src_integer_rect.x - src_integer_rectangle.x; clipped_xscale_position = xscale_factor * x_scale_pixel_multiply; } if(src_integer_rect.y > src_integer_rectangle.y) { int32 y_scale_pixel_multiply = src_integer_rect.y - src_integer_rectangle.y; yscale_position = yscale_factor * y_scale_pixel_multiply; } if(dst_integer_rect.w > 0 && dst_integer_rect.h > 0) { uint8 *src_data = src_bits; uint8 *dst_data = (uint8 *)dest_bits + dst_integer_rect.y * dest_row + dst_integer_rect.x * colorspace_size; while(dst_integer_rect.h--) { xscale_position = clipped_xscale_position; uint8 *s = (uint8 *)((uint8 *)src_data + (yscale_position >> 16) * src_row); uint8 *d = (uint8 *)((uint8 *)dst_data); blitter.Draw(s, d, dst_integer_rect.w, xscale_position, xscale_factor); #if 0 for(int32 x = 0; x < dst_integer_rect.w; x++) { *d++ = s[xscale_position >> 16]; xscale_position += xscale_factor; } #endif dst_data += dest_row; yscale_position += yscale_factor; } } } fCursorHandler.DriverShow(); ReleaseBuffer(); Unlock(); Invalidate(destrect); } /*! \brief Called for all BView::DrawBitmap calls \param bmp Bitmap to be drawn. It will always be non-NULL and valid. The color space is not guaranteed to match. \param src Source rectangle \param dest Destination rectangle. Source will be scaled to fit if not the same size. \param d Data structure containing any other data necessary for the call. Always non-NULL. */ void DisplayDriver::DrawBitmap(ServerBitmap *bmp, const BRect &src, const BRect &dest, const DrawData *d) { /* Lock(); FBBitmap frameBuffer; //FBBitmap *fbmp = &frameBuffer; if(!AcquireBuffer(&frameBuffer)) { debugger("ERROR: Couldn't acquire framebuffer in DrawBitmap()\n"); return; } ReleaseBuffer(); Unlock(); Invalidate(dest); */ } void DisplayDriver::CopyRegionList(BList* list, BList* pList, int32 rCount, BRegion* clipReg) { Lock(); FBBitmap frameBuffer; FBBitmap *bmp = &frameBuffer; if(!AcquireBuffer(&frameBuffer)) { debugger("ERROR: Couldn't acquire framebuffer in CopyRegionList()\n"); Unlock(); return; } fCursorHandler.DriverHide(); uint32 bytesPerPixel = bmp->BytesPerRow() / bmp->Bounds().IntegerWidth(); BList rectList; int32 i, k; uint8 *bitmapBits = (uint8*)bmp->Bits(); int32 Bwidth = bmp->Bounds().IntegerWidth() + 1; int32 Bheight = bmp->Bounds().IntegerHeight() + 1; for(k=0; k < rCount; k++) { BRegion *reg = (BRegion*)list->ItemAt(k); int32 rectCount = reg->CountRects(); for(i=0; i < rectCount; i++) { BRect r = reg->RectAt(i); uint8 *rectCopy; uint8 *srcAddress; uint8 *destAddress; int32 firstRow, lastRow; int32 firstCol, lastCol; int32 copyLength; int32 copyRows; firstRow = (int32)(r.top < 0? 0: r.top); lastRow = (int32)(r.bottom > (Bheight-1)? (Bheight-1): r.bottom); firstCol = (int32)(r.left < 0? 0: r.left); lastCol = (int32)(r.right > (Bwidth-1)? (Bwidth-1): r.right); copyLength = (lastCol - firstCol + 1) < 0? 0: (lastCol - firstCol + 1); copyRows = (lastRow - firstRow + 1) < 0? 0: (lastRow - firstRow + 1); rectCopy = (uint8*)malloc(copyLength * copyRows * bytesPerPixel); srcAddress = bitmapBits + (((firstRow) * Bwidth + firstCol) * bytesPerPixel); destAddress = rectCopy; for (int32 j = 0; j < copyRows; j++) { uint8 *destRowAddress = destAddress + (j * copyLength * bytesPerPixel); uint8 *srcRowAddress = srcAddress + (j * Bwidth * bytesPerPixel); memcpy(destRowAddress, srcRowAddress, copyLength * bytesPerPixel ); } rectList.AddItem(rectCopy); } } int32 item = 0; for(k=0; k < rCount; k++) { BRegion *reg = (BRegion*)list->ItemAt(k); int32 rectCount = reg->CountRects(); for(i=0; i < rectCount; i++) { BRect r = reg->RectAt(i); uint8 *rectCopy; uint8 *srcAddress; uint8 *destAddress; int32 firstRow, lastRow; int32 firstCol, lastCol; int32 copyLength, copyLength2; int32 copyRows, copyRows2; firstRow = (int32)(r.top < 0? 0: r.top); lastRow = (int32)(r.bottom > (Bheight-1)? (Bheight-1): r.bottom); firstCol = (int32)(r.left < 0? 0: r.left); lastCol = (int32)(r.right > (Bwidth-1)? (Bwidth-1): r.right); copyLength = (lastCol - firstCol + 1) < 0? 0: (lastCol - firstCol + 1); copyRows = (lastRow - firstRow + 1) < 0? 0: (lastRow - firstRow + 1); rectCopy = (uint8*)rectList.ItemAt(item++); srcAddress = rectCopy; r.Set(firstCol, firstRow, lastCol, lastRow); r.OffsetBy( *((BPoint*)pList->ItemAt(k%rCount)) ); firstRow = (int32)(r.top < 0? 0: r.top); lastRow = (int32)(r.bottom > (Bheight-1)? (Bheight-1): r.bottom); firstCol = (int32)(r.left < 0? 0: r.left); lastCol = (int32)(r.right > (Bwidth-1)? (Bwidth-1): r.right); copyLength2 = (lastCol - firstCol + 1) < 0? 0: (lastCol - firstCol + 1); copyRows2 = (lastRow - firstRow + 1) < 0? 0: (lastRow - firstRow + 1); destAddress = bitmapBits + (((firstRow) * Bwidth + firstCol) * bytesPerPixel); int32 minLength = copyLength < copyLength2? copyLength: copyLength2; int32 minRows = copyRows < copyRows2? copyRows: copyRows2; for (int32 j = 0; j < minRows; j++) { uint8 *destRowAddress = destAddress + (j * Bwidth * bytesPerPixel); uint8 *srcRowAddress = srcAddress + (j * copyLength * bytesPerPixel); memcpy(destRowAddress, srcRowAddress, minLength * bytesPerPixel ); } } } for(i=0; i < rectList.CountItems(); i++) { void *rectCopy; rectCopy = rectList.ItemAt(i); if (rectCopy) free(rectCopy); } rectList.MakeEmpty(); fCursorHandler.DriverShow(); BRect inval(bmp->Bounds()); ReleaseBuffer(); Unlock(); // ConstrainClippingRegion(clipReg); Invalidate(inval); // ConstrainClippingRegion(NULL); } void DisplayDriver::DrawString(const char *string, const int32 &length, const BPoint &pt, const RGBColor &color, escapement_delta *delta) { DrawData d; d.highcolor=color; if(delta) d.edelta=*delta; DrawString(string,length,pt,&d); } /*! \brief Utilizes the font engine to draw a string to the frame buffer \param string String to be drawn. Always non-NULL. \param length Number of characters in the string to draw. Always greater than 0. If greater than the number of characters in the string, draw the entire string. \param pt Point at which the baseline starts. Characters are to be drawn 1 pixel above this for backwards compatibility. While the point itself is guaranteed to be inside the frame buffers coordinate range, the clipping of each individual glyph must be performed by the driver itself. \param d Data structure containing any other data necessary for the call. Always non-NULL. */ void DisplayDriver::DrawString(const char *string, const int32 &length, const BPoint &pt, DrawData *d) { if(!string || !d) return; Lock(); // TODO: properly calculate intersecting rectangle with cursor in DisplayDriver::DrawString // Rough guesstimate for size BRect intersection(pt.x,pt.x,pt.y,pt.y); intersection.top-=d->font.Size()*1.5; intersection.right+=d->font.Size()*1.5*length; if(fCursorHandler.IntersectsCursor(intersection)) fCursorHandler.DriverHide(); BPoint point(pt); const ServerFont *font=&(d->font); FT_Face face; FT_GlyphSlot slot; FT_Matrix rmatrix,smatrix; FT_UInt glyph_index=0, previous=0; FT_Vector pen,delta,space,nonspace; int16 error=0; int32 strlength,i; Angle rotation(font->Rotation()), shear(font->Shear()); bool antialias=true; if(font->Size()<18 && (font->Flags()& B_DISABLE_ANTIALIASING==1)) antialias=false; // Originally, I thought to do this shear checking here, but it really should be // done in BFont::SetShear() float shearangle=shear.Value(); if(shearangle>135) shearangle=135; if(shearangle<45) shearangle=45; if(shearangle>90) shear=90+((180-shearangle)*2); else shear=90-(90-shearangle)*2; error=FT_New_Face(ftlib, font->GetPath(), 0, &face); if(error) { Unlock(); return; } slot=face->glyph; bool use_kerning=FT_HAS_KERNING(face) && font->Spacing()==B_STRING_SPACING; error=FT_Set_Char_Size(face, 0,int32(font->Size())*64,72,72); if(error) { Unlock(); return; } // if we do any transformation, we do a call to FT_Set_Transform() here // First, rotate rmatrix.xx = (FT_Fixed)( rotation.Cosine()*0x10000); rmatrix.xy = (FT_Fixed)(-rotation.Sine()*0x10000); rmatrix.yx = (FT_Fixed)( rotation.Sine()*0x10000); rmatrix.yy = (FT_Fixed)( rotation.Cosine()*0x10000); // Next, shear smatrix.xx = (FT_Fixed)(0x10000); smatrix.xy = (FT_Fixed)(-shear.Cosine()*0x10000); smatrix.yx = (FT_Fixed)(0); smatrix.yy = (FT_Fixed)(0x10000); FT_Matrix_Multiply(&rmatrix,&smatrix); // Set up the increment value for escapement padding space.x=int32(d->edelta.space * rotation.Cosine()*64); space.y=int32(d->edelta.space * rotation.Sine()*64); nonspace.x=int32(d->edelta.nonspace * rotation.Cosine()*64); nonspace.y=int32(d->edelta.nonspace * rotation.Sine()*64); // set the pen position in 26.6 cartesian space coordinates pen.x=(int32)point.x * 64; pen.y=(int32)point.y * 64; slot=face->glyph; strlength=strlen(string); if(lengthbitmap, BPoint(slot->bitmap_left,point.y-(slot->bitmap_top-point.y)), d); else BlitMono2RGB32(&slot->bitmap, BPoint(slot->bitmap_left,point.y-(slot->bitmap_top-point.y)), d); } // increment pen position pen.x+=slot->advance.x; pen.y+=slot->advance.y; previous=glyph_index; } // TODO: implement calculation of invalid rectangle in DisplayDriver::DrawString properly BRect r; r.left=MIN(point.x,pen.x>>6); r.right=MAX(point.x,pen.x>>6); r.top=point.y-face->height; r.bottom=point.y+face->height; fCursorHandler.DriverShow(); Invalidate(r); // Update the caller's pen position d->penlocation.x=pen.x / 64; d->penlocation.y=pen.y / 64; FT_Done_Face(face); Unlock(); } void DisplayDriver::BlitMono2RGB32(FT_Bitmap *src, const BPoint &pt, const DrawData *d) { rgb_color color=d->highcolor.GetColor32(); // pointers to the top left corner of the area to be copied in each bitmap uint8 *srcbuffer, *destbuffer; FBBitmap framebuffer; if(!AcquireBuffer(&framebuffer)) { printf("ERROR: Couldn't acquire framebuffer in BlitMono2RGB32\n"); return; } // index pointers which are incremented during the course of the blit uint8 *srcindex, *destindex, *rowptr, value; // increment values for the index pointers int32 srcinc=src->pitch, destinc=framebuffer.BytesPerRow(); int16 i,j,k, srcwidth=src->pitch, srcheight=src->rows; int32 x=(int32)pt.x,y=(int32)pt.y; // starting point in source bitmap srcbuffer=(uint8*)src->buffer; if(y<0) { if(yframebuffer.Bounds().IntegerHeight()) { if(y>pt.y) y--; srcheight-=(y+srcheight-1)-framebuffer.Bounds().IntegerHeight(); } if(x+srcwidth>framebuffer.Bounds().IntegerWidth()) { if(x>pt.x) x--; srcwidth-=(x+srcwidth-1)-framebuffer.Bounds().IntegerWidth(); } if(x<0) { if(x>3; srcwidth-=0-x; destbuffer+=(0-x)*4; } // starting point in destination bitmap destbuffer=(uint8*)framebuffer.Bits()+int32( (pt.y*framebuffer.BytesPerRow())+(pt.x*4) ); srcindex=srcbuffer; destindex=destbuffer; for(i=0; ihighcolor.GetColor32(), lowcolor=d->lowcolor.GetColor32(); float rstep,gstep,bstep,astep; rstep=float(highcolor.red-lowcolor.red)/255.0; gstep=float(highcolor.green-lowcolor.green)/255.0; bstep=float(highcolor.blue-lowcolor.blue)/255.0; astep=float(highcolor.alpha-lowcolor.alpha)/255.0; // increment values for the index pointers int32 x=(int32)pt.x, y=(int32)pt.y, srcinc=src->pitch, // destinc=dest->BytesPerRow(), destinc=framebuffer.BytesPerRow(), srcwidth=src->width, srcheight=src->rows, incval=0; int16 i,j; // starting point in source bitmap srcbuffer=(uint8*)src->buffer; // starting point in destination bitmap destbuffer=(uint8*)framebuffer.Bits()+(y*framebuffer.BytesPerRow()+(x*4)); if(y<0) { if(yframebuffer.Bounds().IntegerHeight()) { if(y>pt.y) y--; srcheight-=(y+srcheight-1)-framebuffer.Bounds().IntegerHeight(); } if(x+srcwidth>framebuffer.Bounds().IntegerWidth()) { if(x>pt.x) x--; srcwidth-=(x+srcwidth-1)-framebuffer.Bounds().IntegerWidth(); } if(x<0) { if(xdraw_mode==B_OP_COPY) { rowptr[0]=uint8(highcolor.blue-(value*bstep)); rowptr[1]=uint8(highcolor.green-(value*gstep)); rowptr[2]=uint8(highcolor.red-(value*rstep)); rowptr[3]=255; } else if(d->draw_mode==B_OP_OVER) { if(highcolor.alpha>127) { rowptr[0]=uint8(highcolor.blue-(value*(float(highcolor.blue-rowptr[0])/255.0))); rowptr[1]=uint8(highcolor.green-(value*(float(highcolor.green-rowptr[1])/255.0))); rowptr[2]=uint8(highcolor.red-(value*(float(highcolor.red-rowptr[2])/255.0))); rowptr[3]=255; } } } rowptr+=4; } srcindex+=srcinc; destindex+=destinc; } ReleaseBuffer(); } bool DisplayDriver::AcquireBuffer(FBBitmap *bmp) { return false; } void DisplayDriver::ReleaseBuffer(void) { } void DisplayDriver::Invalidate(const BRect &r) { } /*! \brief Called for all BView::FillArc calls \param r Rectangle enclosing the entire arc \param angle Starting angle for the arc in degrees \param span Span of the arc in degrees. Ending angle = angle+span. \param color The color of the arc */ void DisplayDriver::FillArc(const BRect &r, const float &angle, const float &span, const RGBColor &color) { if(fCursorHandler.IntersectsCursor(r)) fCursorHandler.DriverHide(); float xc = (r.left+r.right)/2; float yc = (r.top+r.bottom)/2; float rx = r.Width()/2; float ry = r.Height()/2; int Rx2 = ROUND(rx*rx); int Ry2 = ROUND(ry*ry); int twoRx2 = 2*Rx2; int twoRy2 = 2*Ry2; int p; int x=0; int y = (int)ry; int px = 0; int py = twoRx2 * y; int startx, endx; int starty, endy; int xclip, startclip, endclip; int startQuad, endQuad; bool useQuad1, useQuad2, useQuad3, useQuad4; bool shortspan = false; // Watch out for bozos giving us whacko spans if ( (span >= 360) || (span <= -360) ) { FillEllipse(r,color); fCursorHandler.DriverShow(); return; } Lock(); if ( span > 0 ) { startQuad = (int)(angle/90)%4+1; endQuad = (int)((angle+span)/90)%4+1; startx = ROUND(.5*r.Width()*fabs(cos(angle*M_PI/180))); endx = ROUND(.5*r.Width()*fabs(cos((angle+span)*M_PI/180))); } else { endQuad = (int)(angle/90)%4+1; startQuad = (int)((angle+span)/90)%4+1; endx = ROUND(.5*r.Width()*fabs(cos(angle*M_PI/180))); startx = ROUND(.5*r.Width()*fabs(cos((angle+span)*M_PI/180))); } starty = ROUND(ry*sqrt(1-(double)startx*startx/(rx*rx))); endy = ROUND(ry*sqrt(1-(double)endx*endx/(rx*rx))); if ( startQuad != endQuad ) { useQuad1 = (endQuad > 1) && (startQuad > endQuad); useQuad2 = ((startQuad == 1) && (endQuad > 2)) || ((startQuad > endQuad) && (endQuad > 2)); useQuad3 = ((startQuad < 3) && (endQuad == 4)) || ((startQuad < 3) && (endQuad < startQuad)); useQuad4 = (startQuad < 4) && (startQuad > endQuad); } else { if ( (span < 90) && (span > -90) ) { useQuad1 = false; useQuad2 = false; useQuad3 = false; useQuad4 = false; shortspan = true; } else { useQuad1 = (startQuad != 1); useQuad2 = (startQuad != 2); useQuad3 = (startQuad != 3); useQuad4 = (startQuad != 4); } } if ( useQuad1 ) StrokeSolidLine(ROUND(xc),ROUND(yc-y),ROUND(xc+x),ROUND(yc-y),color); if ( useQuad2 ) StrokeSolidLine(ROUND(xc),ROUND(yc-y),ROUND(xc-x),ROUND(yc-y),color); if ( useQuad3 ) StrokeSolidLine(ROUND(xc),ROUND(yc+y),ROUND(xc-x),ROUND(yc+y),color); if ( useQuad4 ) StrokeSolidLine(ROUND(xc),ROUND(yc+y),ROUND(xc+x),ROUND(yc+y),color); p = ROUND (Ry2 - (Rx2 * ry) + (.25 * Rx2)); while (px < py) { x++; px += twoRy2; if ( p < 0 ) p += Ry2 + px; else { y--; py -= twoRx2; p += Ry2 + px - py; } if ( useQuad1 ) StrokeSolidLine(ROUND(xc),ROUND(yc-y),ROUND(xc+x),ROUND(yc-y),color); if ( useQuad2 ) StrokeSolidLine(ROUND(xc),ROUND(yc-y),ROUND(xc-x),ROUND(yc-y),color); if ( useQuad3 ) StrokeSolidLine(ROUND(xc),ROUND(yc+y),ROUND(xc-x),ROUND(yc+y),color); if ( useQuad4 ) StrokeSolidLine(ROUND(xc),ROUND(yc+y),ROUND(xc+x),ROUND(yc+y),color); if ( !shortspan ) { if ( startQuad == 1 ) { if ( x <= startx ) StrokeSolidLine(ROUND(xc),ROUND(yc-y),ROUND(xc+x),ROUND(yc-y),color); else { xclip = ROUND(y*startx/(double)starty); StrokeSolidLine(ROUND(xc),ROUND(yc-y),ROUND(xc+xclip),ROUND(yc-y),color); } } else if ( startQuad == 2 ) { if ( x >= startx ) { xclip = ROUND(y*startx/(double)starty); StrokeSolidLine(ROUND(xc-x),ROUND(yc-y),ROUND(xc-xclip),ROUND(yc-y),color); } } else if ( startQuad == 3 ) { if ( x <= startx ) StrokeSolidLine(ROUND(xc-x),ROUND(yc+y),ROUND(xc),ROUND(yc+y),color); else { xclip = ROUND(y*startx/(double)starty); StrokeSolidLine(ROUND(xc-xclip),ROUND(yc+y),ROUND(xc),ROUND(yc+y),color); } } else if ( startQuad == 4 ) { if ( x >= startx ) { xclip = ROUND(y*startx/(double)starty); StrokeSolidLine(ROUND(xc+xclip),ROUND(yc+y),ROUND(xc+x),ROUND(yc+y),color); } } if ( endQuad == 1 ) { if ( x >= endx ) { xclip = ROUND(y*endx/(double)endy); StrokeSolidLine(ROUND(xc+xclip),ROUND(yc-y),ROUND(xc+x),ROUND(yc-y),color); } } else if ( endQuad == 2 ) { if ( x <= endx ) StrokeSolidLine(ROUND(xc-x),ROUND(yc-y),ROUND(xc),ROUND(yc-y),color); else { xclip = ROUND(y*endx/(double)endy); StrokeSolidLine(ROUND(xc-xclip),ROUND(yc-y),ROUND(xc),ROUND(yc-y),color); } } else if ( endQuad == 3 ) { if ( x >= endx ) { xclip = ROUND(y*endx/(double)endy); StrokeSolidLine(ROUND(xc-x),ROUND(yc+y),ROUND(xc-xclip),ROUND(yc+y),color); } } else if ( endQuad == 4 ) { if ( x <= endx ) StrokeSolidLine(ROUND(xc),ROUND(yc+y),ROUND(xc+x),ROUND(yc+y),color); else { xclip = ROUND(y*endx/(double)endy); StrokeSolidLine(ROUND(xc),ROUND(yc+y),ROUND(xc+xclip),ROUND(yc+y),color); } } } else { startclip = ROUND(y*startx/(double)starty); endclip = ROUND(y*endx/(double)endy); if ( startQuad == 1 ) { if ( (x <= startx) && (x >= endx) ) StrokeSolidLine(ROUND(xc+endclip),ROUND(yc-y),ROUND(xc+x),ROUND(yc-y),color); else StrokeSolidLine(ROUND(xc+endclip),ROUND(yc-y),ROUND(xc+startclip),ROUND(yc-y),color); } else if ( startQuad == 2 ) { if ( (x <= startx) && (x >= endx) ) StrokeSolidLine(ROUND(xc-x),ROUND(yc-y),ROUND(xc-startclip),ROUND(yc-y),color); else StrokeSolidLine(ROUND(xc-endclip),ROUND(yc-y),ROUND(xc-startclip),ROUND(yc-y),color); } else if ( startQuad == 3 ) { if ( (x <= startx) && (x >= endx) ) StrokeSolidLine(ROUND(xc-x),ROUND(yc+y),ROUND(xc-endclip),ROUND(yc+y),color); else StrokeSolidLine(ROUND(xc-startclip),ROUND(yc+y),ROUND(xc-endclip),ROUND(yc+y),color); } else if ( startQuad == 4 ) { if ( (x <= startx) && (x >= endx) ) StrokeSolidLine(ROUND(xc+startclip),ROUND(yc+y),ROUND(xc+x),ROUND(yc+y),color); else StrokeSolidLine(ROUND(xc+startclip),ROUND(yc+y),ROUND(xc+endclip),ROUND(yc+y),color); } } } p = ROUND(Ry2*(x+.5)*(x+.5) + Rx2*(y-1)*(y-1) - Rx2*Ry2); while (y>0) { y--; py -= twoRx2; if (p>0) p += Rx2 - py; else { x++; px += twoRy2; p += Rx2 - py +px; } if ( useQuad1 ) StrokeSolidLine(ROUND(xc),ROUND(yc-y),ROUND(xc+x),ROUND(yc-y),color); if ( useQuad2 ) StrokeSolidLine(ROUND(xc),ROUND(yc-y),ROUND(xc-x),ROUND(yc-y),color); if ( useQuad3 ) StrokeSolidLine(ROUND(xc),ROUND(yc+y),ROUND(xc-x),ROUND(yc+y),color); if ( useQuad4 ) StrokeSolidLine(ROUND(xc),ROUND(yc+y),ROUND(xc+x),ROUND(yc+y),color); if ( !shortspan ) { if ( startQuad == 1 ) { if ( x <= startx ) StrokeSolidLine(ROUND(xc),ROUND(yc-y),ROUND(xc+x),ROUND(yc-y),color); else { xclip = ROUND(y*startx/(double)starty); StrokeSolidLine(ROUND(xc),ROUND(yc-y),ROUND(xc+xclip),ROUND(yc-y),color); } } else if ( startQuad == 2 ) { if ( x >= startx ) { xclip = ROUND(y*startx/(double)starty); StrokeSolidLine(ROUND(xc-x),ROUND(yc-y),ROUND(xc-xclip),ROUND(yc-y),color); } } else if ( startQuad == 3 ) { if ( x <= startx ) StrokeSolidLine(ROUND(xc-x),ROUND(yc+y),ROUND(xc),ROUND(yc+y),color); else { xclip = ROUND(y*startx/(double)starty); StrokeSolidLine(ROUND(xc-xclip),ROUND(yc+y),ROUND(xc),ROUND(yc+y),color); } } else if ( startQuad == 4 ) { if ( x >= startx ) { xclip = ROUND(y*startx/(double)starty); StrokeSolidLine(ROUND(xc+xclip),ROUND(yc+y),ROUND(xc+x),ROUND(yc+y),color); } } if ( endQuad == 1 ) { if ( x >= endx ) { xclip = ROUND(y*endx/(double)endy); StrokeSolidLine(ROUND(xc+xclip),ROUND(yc-y),ROUND(xc+x),ROUND(yc-y),color); } } else if ( endQuad == 2 ) { if ( x <= endx ) StrokeSolidLine(ROUND(xc-x),ROUND(yc-y),ROUND(xc),ROUND(yc-y),color); else { xclip = ROUND(y*endx/(double)endy); StrokeSolidLine(ROUND(xc-xclip),ROUND(yc-y),ROUND(xc),ROUND(yc-y),color); } } else if ( endQuad == 3 ) { if ( x >= endx ) { xclip = ROUND(y*endx/(double)endy); StrokeSolidLine(ROUND(xc-x),ROUND(yc+y),ROUND(xc-xclip),ROUND(yc+y),color); } } else if ( endQuad == 4 ) { if ( x <= endx ) StrokeSolidLine(ROUND(xc),ROUND(yc+y),ROUND(xc+x),ROUND(yc+y),color); else { xclip = ROUND(y*endx/(double)endy); StrokeSolidLine(ROUND(xc),ROUND(yc+y),ROUND(xc+xclip),ROUND(yc+y),color); } } } else { startclip = ROUND(y*startx/(double)starty); endclip = ROUND(y*endx/(double)endy); if ( startQuad == 1 ) { if ( (x <= startx) && (x >= endx) ) StrokeSolidLine(ROUND(xc+endclip),ROUND(yc-y),ROUND(xc+x),ROUND(yc-y),color); else StrokeSolidLine(ROUND(xc+endclip),ROUND(yc-y),ROUND(xc+startclip),ROUND(yc-y),color); } else if ( startQuad == 2 ) { if ( (x <= startx) && (x >= endx) ) StrokeSolidLine(ROUND(xc-x),ROUND(yc-y),ROUND(xc-startclip),ROUND(yc-y),color); else StrokeSolidLine(ROUND(xc-endclip),ROUND(yc-y),ROUND(xc-startclip),ROUND(yc-y),color); } else if ( startQuad == 3 ) { if ( (x <= startx) && (x >= endx) ) StrokeSolidLine(ROUND(xc-x),ROUND(yc+y),ROUND(xc-endclip),ROUND(yc+y),color); else StrokeSolidLine(ROUND(xc-startclip),ROUND(yc+y),ROUND(xc-endclip),ROUND(yc+y),color); } else if ( startQuad == 4 ) { if ( (x <= startx) && (x >= endx) ) StrokeSolidLine(ROUND(xc+startclip),ROUND(yc+y),ROUND(xc+x),ROUND(yc+y),color); else StrokeSolidLine(ROUND(xc+startclip),ROUND(yc+y),ROUND(xc+endclip),ROUND(yc+y),color); } } } fCursorHandler.DriverShow(); Invalidate(r); Unlock(); } /*! \brief Called for all BView::FillArc calls \param r Rectangle enclosing the entire arc \param angle Starting angle for the arc in degrees \param span Span of the arc in degrees. Ending angle = angle+span. \param d Object holding the bazillion other options */ void DisplayDriver::FillArc(const BRect &r, const float &angle, const float &span, const DrawData *d) { if(fCursorHandler.IntersectsCursor(r)) fCursorHandler.DriverHide(); float xc = (r.left+r.right)/2; float yc = (r.top+r.bottom)/2; float rx = r.Width()/2; float ry = r.Height()/2; int Rx2 = ROUND(rx*rx); int Ry2 = ROUND(ry*ry); int twoRx2 = 2*Rx2; int twoRy2 = 2*Ry2; int p; int x=0; int y = (int)ry; int px = 0; int py = twoRx2 * y; int startx, endx; int starty, endy; int xclip, startclip, endclip; int startQuad, endQuad; bool useQuad1, useQuad2, useQuad3, useQuad4; bool shortspan = false; DrawData data; // Watch out for bozos giving us whacko spans if ( (span >= 360) || (span <= -360) ) { FillEllipse(r,d); fCursorHandler.DriverShow(); return; } Lock(); data = *d; data.pensize = 1; if ( span > 0 ) { startQuad = (int)(angle/90)%4+1; endQuad = (int)((angle+span)/90)%4+1; startx = ROUND(.5*r.Width()*fabs(cos(angle*M_PI/180))); endx = ROUND(.5*r.Width()*fabs(cos((angle+span)*M_PI/180))); } else { endQuad = (int)(angle/90)%4+1; startQuad = (int)((angle+span)/90)%4+1; endx = ROUND(.5*r.Width()*fabs(cos(angle*M_PI/180))); startx = ROUND(.5*r.Width()*fabs(cos((angle+span)*M_PI/180))); } starty = ROUND(ry*sqrt(1-(double)startx*startx/(rx*rx))); endy = ROUND(ry*sqrt(1-(double)endx*endx/(rx*rx))); if ( startQuad != endQuad ) { useQuad1 = (endQuad > 1) && (startQuad > endQuad); useQuad2 = ((startQuad == 1) && (endQuad > 2)) || ((startQuad > endQuad) && (endQuad > 2)); useQuad3 = ((startQuad < 3) && (endQuad == 4)) || ((startQuad < 3) && (endQuad < startQuad)); useQuad4 = (startQuad < 4) && (startQuad > endQuad); } else { if ( (span < 90) && (span > -90) ) { useQuad1 = false; useQuad2 = false; useQuad3 = false; useQuad4 = false; shortspan = true; } else { useQuad1 = (startQuad != 1); useQuad2 = (startQuad != 2); useQuad3 = (startQuad != 3); useQuad4 = (startQuad != 4); } } if ( useQuad1 ) StrokeLine(BPoint(xc,yc-y),BPoint(xc+x,yc-y),&data); if ( useQuad2 ) StrokeLine(BPoint(xc,yc-y),BPoint(xc-x,yc-y),&data); if ( useQuad3 ) StrokeLine(BPoint(xc,yc+y),BPoint(xc-x,yc+y),&data); if ( useQuad4 ) StrokeLine(BPoint(xc,yc+y),BPoint(xc+x,yc+y),&data); p = ROUND (Ry2 - (Rx2 * ry) + (.25 * Rx2)); while (px < py) { x++; px += twoRy2; if ( p < 0 ) p += Ry2 + px; else { y--; py -= twoRx2; p += Ry2 + px - py; } if ( useQuad1 ) StrokeLine(BPoint(xc,yc-y),BPoint(xc+x,yc-y),&data); if ( useQuad2 ) StrokeLine(BPoint(xc,yc-y),BPoint(xc-x,yc-y),&data); if ( useQuad3 ) StrokeLine(BPoint(xc,yc+y),BPoint(xc-x,yc+y),&data); if ( useQuad4 ) StrokeLine(BPoint(xc,yc+y),BPoint(xc+x,yc+y),&data); if ( !shortspan ) { if ( startQuad == 1 ) { if ( x <= startx ) StrokeLine(BPoint(xc,yc-y),BPoint(xc+x,yc-y),&data); else { xclip = ROUND(y*startx/(double)starty); StrokeLine(BPoint(xc,yc-y),BPoint(xc+xclip,yc-y),&data); } } else if ( startQuad == 2 ) { if ( x >= startx ) { xclip = ROUND(y*startx/(double)starty); StrokeLine(BPoint(xc-x,yc-y),BPoint(xc-xclip,yc-y),&data); } } else if ( startQuad == 3 ) { if ( x <= startx ) StrokeLine(BPoint(xc-x,yc+y),BPoint(xc,yc+y),&data); else { xclip = ROUND(y*startx/(double)starty); StrokeLine(BPoint(xc-xclip,yc+y),BPoint(xc,yc+y),&data); } } else if ( startQuad == 4 ) { if ( x >= startx ) { xclip = ROUND(y*startx/(double)starty); StrokeLine(BPoint(xc+xclip,yc+y),BPoint(xc+x,yc+y),&data); } } if ( endQuad == 1 ) { if ( x >= endx ) { xclip = ROUND(y*endx/(double)endy); StrokeLine(BPoint(xc+xclip,yc-y),BPoint(xc+x,yc-y),&data); } } else if ( endQuad == 2 ) { if ( x <= endx ) StrokeLine(BPoint(xc-x,yc-y),BPoint(xc,yc-y),&data); else { xclip = ROUND(y*endx/(double)endy); StrokeLine(BPoint(xc-xclip,yc-y),BPoint(xc,yc-y),&data); } } else if ( endQuad == 3 ) { if ( x >= endx ) { xclip = ROUND(y*endx/(double)endy); StrokeLine(BPoint(xc-x,yc+y),BPoint(xc-xclip,yc+y),&data); } } else if ( endQuad == 4 ) { if ( x <= endx ) StrokeLine(BPoint(xc,yc+y),BPoint(xc+x,yc+y),&data); else { xclip = ROUND(y*endx/(double)endy); StrokeLine(BPoint(xc,yc+y),BPoint(xc+xclip,yc+y),&data); } } } else { startclip = ROUND(y*startx/(double)starty); endclip = ROUND(y*endx/(double)endy); if ( startQuad == 1 ) { if ( (x <= startx) && (x >= endx) ) StrokeLine(BPoint(xc+endclip,yc-y),BPoint(xc+x,yc-y),&data); else StrokeLine(BPoint(xc+endclip,yc-y),BPoint(xc+startclip,yc-y),&data); } else if ( startQuad == 2 ) { if ( (x <= startx) && (x >= endx) ) StrokeLine(BPoint(xc-x,yc-y),BPoint(xc-startclip,yc-y),&data); else StrokeLine(BPoint(xc-endclip,yc-y),BPoint(xc-startclip,yc-y),&data); } else if ( startQuad == 3 ) { if ( (x <= startx) && (x >= endx) ) StrokeLine(BPoint(xc-x,yc+y),BPoint(xc-endclip,yc+y),&data); else StrokeLine(BPoint(xc-startclip,yc+y),BPoint(xc-endclip,yc+y),&data); } else if ( startQuad == 4 ) { if ( (x <= startx) && (x >= endx) ) StrokeLine(BPoint(xc+startclip,yc+y),BPoint(xc+x,yc+y),&data); else StrokeLine(BPoint(xc+startclip,yc+y),BPoint(xc+endclip,yc+y),&data); } } } p = ROUND(Ry2*(x+.5)*(x+.5) + Rx2*(y-1)*(y-1) - Rx2*Ry2); while (y>0) { y--; py -= twoRx2; if (p>0) p += Rx2 - py; else { x++; px += twoRy2; p += Rx2 - py +px; } if ( useQuad1 ) StrokeLine(BPoint(xc,yc-y),BPoint(xc+x,yc-y),&data); if ( useQuad2 ) StrokeLine(BPoint(xc,yc-y),BPoint(xc-x,yc-y),&data); if ( useQuad3 ) StrokeLine(BPoint(xc,yc+y),BPoint(xc-x,yc+y),&data); if ( useQuad4 ) StrokeLine(BPoint(xc,yc+y),BPoint(xc+x,yc+y),&data); if ( !shortspan ) { if ( startQuad == 1 ) { if ( x <= startx ) StrokeLine(BPoint(xc,yc-y),BPoint(xc+x,yc-y),&data); else { xclip = ROUND(y*startx/(double)starty); StrokeLine(BPoint(xc,yc-y),BPoint(xc+xclip,yc-y),&data); } } else if ( startQuad == 2 ) { if ( x >= startx ) { xclip = ROUND(y*startx/(double)starty); StrokeLine(BPoint(xc-x,yc-y),BPoint(xc-xclip,yc-y),&data); } } else if ( startQuad == 3 ) { if ( x <= startx ) StrokeLine(BPoint(xc-x,yc+y),BPoint(xc,yc+y),&data); else { xclip = ROUND(y*startx/(double)starty); StrokeLine(BPoint(xc-xclip,yc+y),BPoint(xc,yc+y),&data); } } else if ( startQuad == 4 ) { if ( x >= startx ) { xclip = ROUND(y*startx/(double)starty); StrokeLine(BPoint(xc+xclip,yc+y),BPoint(xc+x,yc+y),&data); } } if ( endQuad == 1 ) { if ( x >= endx ) { xclip = ROUND(y*endx/(double)endy); StrokeLine(BPoint(xc+xclip,yc-y),BPoint(xc+x,yc-y),&data); } } else if ( endQuad == 2 ) { if ( x <= endx ) StrokeLine(BPoint(xc-x,yc-y),BPoint(xc,yc-y),&data); else { xclip = ROUND(y*endx/(double)endy); StrokeLine(BPoint(xc-xclip,yc-y),BPoint(xc,yc-y),&data); } } else if ( endQuad == 3 ) { if ( x >= endx ) { xclip = ROUND(y*endx/(double)endy); StrokeLine(BPoint(xc-x,yc+y),BPoint(xc-xclip,yc+y),&data); } } else if ( endQuad == 4 ) { if ( x <= endx ) StrokeLine(BPoint(xc,yc+y),BPoint(xc+x,yc+y),&data); else { xclip = ROUND(y*endx/(double)endy); StrokeLine(BPoint(xc,yc+y),BPoint(xc+xclip,yc+y),&data); } } } else { startclip = ROUND(y*startx/(double)starty); endclip = ROUND(y*endx/(double)endy); if ( startQuad == 1 ) { if ( (x <= startx) && (x >= endx) ) StrokeLine(BPoint(xc+endclip,yc-y),BPoint(xc+x,yc-y),&data); else StrokeLine(BPoint(xc+endclip,yc-y),BPoint(xc+startclip,yc-y),&data); } else if ( startQuad == 2 ) { if ( (x <= startx) && (x >= endx) ) StrokeLine(BPoint(xc-x,yc-y),BPoint(xc-startclip,yc-y),&data); else StrokeLine(BPoint(xc-endclip,yc-y),BPoint(xc-startclip,yc-y),&data); } else if ( startQuad == 3 ) { if ( (x <= startx) && (x >= endx) ) StrokeLine(BPoint(xc-x,yc+y),BPoint(xc-endclip,yc+y),&data); else StrokeLine(BPoint(xc-startclip,yc+y),BPoint(xc-endclip,yc+y),&data); } else if ( startQuad == 4 ) { if ( (x <= startx) && (x >= endx) ) StrokeLine(BPoint(xc+startclip,yc+y),BPoint(xc+x,yc+y),&data); else StrokeLine(BPoint(xc+startclip,yc+y),BPoint(xc+endclip,yc+y),&data); } } } fCursorHandler.DriverShow(); Invalidate(r); Unlock(); } void DisplayDriver::FillBezier(BPoint *pts, const RGBColor &color) { Lock(); BezierCurve curve(pts); if(fCursorHandler.IntersectsCursor(curve.Frame())) fCursorHandler.DriverHide(); FillPolygon(curve.GetPointArray(), curve.points.CountItems(), curve.Frame(), color); fCursorHandler.DriverShow(); Unlock(); } /*! \brief Called for all BView::FillBezier calls. \param pts 4-element array of BPoints in the order of start, end, and then the two control points. \param d draw data */ void DisplayDriver::FillBezier(BPoint *pts, const DrawData *d) { Lock(); BezierCurve curve(pts); if(fCursorHandler.IntersectsCursor(curve.Frame())) fCursorHandler.DriverHide(); FillPolygon(curve.GetPointArray(), curve.points.CountItems(), curve.Frame(), d); fCursorHandler.DriverShow(); Unlock(); } /*! \brief Called for all BView::FillEllipse calls \param r BRect enclosing the ellipse to be drawn. \param color The color of the ellipse */ void DisplayDriver::FillEllipse(const BRect &r, const RGBColor &color) { float xc = (r.left+r.right)/2; float yc = (r.top+r.bottom)/2; float rx = r.Width()/2; float ry = r.Height()/2; int Rx2 = ROUND(rx*rx); int Ry2 = ROUND(ry*ry); int twoRx2 = 2*Rx2; int twoRy2 = 2*Ry2; int p; int x=0; int y = (int)ry; int px = 0; int py = twoRx2 * y; Lock(); if(fCursorHandler.IntersectsCursor(r)) fCursorHandler.DriverHide(); StrokeSolidLine(ROUND(xc),ROUND(yc-y),ROUND(xc),ROUND(yc-y),color); StrokeSolidLine(ROUND(xc),ROUND(yc+y),ROUND(xc),ROUND(yc+y),color); p = ROUND (Ry2 - (Rx2 * ry) + (.25 * Rx2)); while (px < py) { x++; px += twoRy2; if ( p < 0 ) p += Ry2 + px; else { y--; py -= twoRx2; p += Ry2 + px - py; } StrokeSolidLine(ROUND(xc-x),ROUND(yc-y),ROUND(xc+x),ROUND(yc-y),color); StrokeSolidLine(ROUND(xc-x),ROUND(yc+y),ROUND(xc+x),ROUND(yc+y),color); } p = ROUND(Ry2*(x+.5)*(x+.5) + Rx2*(y-1)*(y-1) - Rx2*Ry2); while (y>0) { y--; py -= twoRx2; if (p>0) p += Rx2 - py; else { x++; px += twoRy2; p += Rx2 - py +px; } StrokeSolidLine(ROUND(xc-x),ROUND(yc-y),ROUND(xc+x),ROUND(yc-y),color); StrokeSolidLine(ROUND(xc-x),ROUND(yc+y),ROUND(xc+x),ROUND(yc+y),color); } fCursorHandler.DriverShow(); Invalidate(r); Unlock(); } /*! \brief Called for all BView::FillEllipse calls \param r BRect enclosing the ellipse to be drawn. \param d DrawData containing the endless options */ void DisplayDriver::FillEllipse(const BRect &r, const DrawData *d) { float xc = (r.left+r.right)/2; float yc = (r.top+r.bottom)/2; float rx = r.Width()/2; float ry = r.Height()/2; int Rx2 = ROUND(rx*rx); int Ry2 = ROUND(ry*ry); int twoRx2 = 2*Rx2; int twoRy2 = 2*Ry2; int p; int x=0; int y = (int)ry; int px = 0; int py = twoRx2 * y; DrawData data; Lock(); if(fCursorHandler.IntersectsCursor(r)) fCursorHandler.DriverHide(); data = *d; data.pensize = 1; StrokeLine(BPoint(xc,yc-y),BPoint(xc,yc-y),&data); StrokeLine(BPoint(xc,yc+y),BPoint(xc,yc+y),&data); p = ROUND (Ry2 - (Rx2 * ry) + (.25 * Rx2)); while (px < py) { x++; px += twoRy2; if ( p < 0 ) p += Ry2 + px; else { y--; py -= twoRx2; p += Ry2 + px - py; } StrokeLine(BPoint(xc-x,yc-y),BPoint(xc+x,yc-y),&data); StrokeLine(BPoint(xc-x,yc+y),BPoint(xc+x,yc+y),&data); } p = ROUND(Ry2*(x+.5)*(x+.5) + Rx2*(y-1)*(y-1) - Rx2*Ry2); while (y>0) { y--; py -= twoRx2; if (p>0) p += Rx2 - py; else { x++; px += twoRy2; p += Rx2 - py +px; } StrokeLine(BPoint(xc-x,yc-y),BPoint(xc+x,yc-y),&data); StrokeLine(BPoint(xc-x,yc+y),BPoint(xc+x,yc+y),&data); } fCursorHandler.DriverShow(); Invalidate(r); Unlock(); } /*! \brief Called for all BView::FillPolygon calls \param ptlist Array of BPoints defining the polygon. \param numpts Number of points in the BPoint array. \param color The color of the polygon */ void DisplayDriver::FillPolygon(BPoint *ptlist, int32 numpts, const BRect &bounds, const RGBColor &color) { /* Here's the plan. Record all line segments in polygon. If a line segments crosses the y-value of a point not in the segment, split the segment into 2 segments. Once we have gone through all of the segments, sort them primarily on y-value and secondarily on x-value. Step through each y-value in the bounding rectangle and look for intersections with line segments. First intersection is start of horizontal line, second intersection is end of horizontal line. Continue for all pairs of intersections. Watch out for horizontal line segments. */ if ( !ptlist || (numpts < 3) ) return; Lock(); if(fCursorHandler.IntersectsCursor(bounds)) fCursorHandler.DriverHide(); BPoint *currentPoint, *nextPoint; BPoint tempNextPoint; BPoint tempCurrentPoint; int currentIndex, bestIndex, i, j, y; LineCalc *segmentArray = new LineCalc[2*numpts]; int numSegments = 0; int minX, minY, maxX, maxY; minX = ROUND(ptlist[0].x); maxX = ROUND(ptlist[0].x); minY = ROUND(ptlist[0].y); maxY = ROUND(ptlist[0].y); // Generate the segment list currentPoint = ptlist; currentIndex = 0; nextPoint = &ptlist[1]; while (currentPoint) { if ( numSegments >= 2*numpts ) { printf("ERROR: Insufficient memory allocated to segment array\n"); delete[] segmentArray; Unlock(); return; } if ( ROUND(currentPoint->x) < minX ) minX = ROUND(currentPoint->x); if ( ROUND(currentPoint->x) > maxX ) maxX = ROUND(currentPoint->x); if ( ROUND(currentPoint->y) < minY ) minY = ROUND(currentPoint->y); if ( ROUND(currentPoint->y) > maxY ) maxY = ROUND(currentPoint->y); for (i=0; i currentPoint->y) && (ptlist[i].y < nextPoint->y)) || ((ptlist[i].y < currentPoint->y) && (ptlist[i].y > nextPoint->y)) ) { segmentArray[numSegments].SetPoints(*currentPoint,*nextPoint); tempNextPoint.x = segmentArray[numSegments].GetX(ptlist[i].y); tempNextPoint.y = ptlist[i].y; nextPoint = &tempNextPoint; } } segmentArray[numSegments].SetPoints(*currentPoint,*nextPoint); numSegments++; if ( nextPoint == &tempNextPoint ) { tempCurrentPoint = tempNextPoint; currentPoint = &tempCurrentPoint; nextPoint = &ptlist[(currentIndex+1)%numpts]; } else if ( nextPoint == ptlist ) { currentPoint = NULL; } else { currentPoint = nextPoint; currentIndex++; nextPoint = &ptlist[(currentIndex+1)%numpts]; } } // Selection sort the segments. Probably should replace this later. for (i=0; i y) break; if (segmentArray[i].MaxY() < y) { i++; continue; } if (segmentArray[i].MinY() == segmentArray[i].MaxY()) { if ( (segmentArray[i].MinX() < fDisplayMode.virtual_width) && (segmentArray[i].MaxX() >= 0) ) StrokeSolidLine(ROUND(segmentArray[i].MinX()), y, ROUND(segmentArray[i].MaxX()), y, color); i++; } else { if ( (segmentArray[i+1].GetX(y) < fDisplayMode.virtual_width) && (segmentArray[i].GetX(y) >= 0) ) StrokeSolidLine(ROUND(segmentArray[i].GetX(y)), y, ROUND(segmentArray[i+1].GetX(y)), y, color); i+=2; } } } delete[] segmentArray; fCursorHandler.DriverShow(); Invalidate(bounds); Unlock(); } /*! \brief Called for all BView::FillPolygon calls \param ptlist Array of BPoints defining the polygon. \param numpts Number of points in the BPoint array. \param d The 50 bazillion drawing options (inluding clip region) */ void DisplayDriver::FillPolygon(BPoint *ptlist, int32 numpts, const BRect &bounds, const DrawData *d) { /* Here's the plan. Record all line segments in polygon. If a line segments crosses the y-value of a point not in the segment, split the segment into 2 segments. Once we have gone through all of the segments, sort them primarily on y-value and secondarily on x-value. Step through each y-value in the bounding rectangle and look for intersections with line segments. First intersection is start of horizontal line, second intersection is end of horizontal line. Continue for all pairs of intersections. Watch out for horizontal line segments. */ if ( !ptlist || (numpts < 3) ) return; Lock(); if(fCursorHandler.IntersectsCursor(bounds)) fCursorHandler.DriverHide(); BPoint *currentPoint, *nextPoint; BPoint tempNextPoint; BPoint tempCurrentPoint; int currentIndex, bestIndex, i, j, y; LineCalc *segmentArray = new LineCalc[2*numpts]; int numSegments = 0; int minX, minY, maxX, maxY; minX = ROUND(ptlist[0].x); maxX = ROUND(ptlist[0].x); minY = ROUND(ptlist[0].y); maxY = ROUND(ptlist[0].y); // Generate the segment list currentPoint = ptlist; currentIndex = 0; nextPoint = &ptlist[1]; while (currentPoint) { if ( numSegments >= 2*numpts ) { printf("ERROR: Insufficient memory allocated to segment array\n"); delete[] segmentArray; fCursorHandler.DriverShow(); Unlock(); return; } if ( ROUND(currentPoint->x) < minX ) minX = ROUND(currentPoint->x); if ( ROUND(currentPoint->x) > maxX ) maxX = ROUND(currentPoint->x); if ( ROUND(currentPoint->y) < minY ) minY = ROUND(currentPoint->y); if ( ROUND(currentPoint->y) > maxY ) maxY = ROUND(currentPoint->y); for (i=0; i currentPoint->y) && (ptlist[i].y < nextPoint->y)) || ((ptlist[i].y < currentPoint->y) && (ptlist[i].y > nextPoint->y)) ) { segmentArray[numSegments].SetPoints(*currentPoint,*nextPoint); tempNextPoint.x = segmentArray[numSegments].GetX(ptlist[i].y); tempNextPoint.y = ptlist[i].y; nextPoint = &tempNextPoint; } } segmentArray[numSegments].SetPoints(*currentPoint,*nextPoint); numSegments++; if ( nextPoint == &tempNextPoint ) { tempCurrentPoint = tempNextPoint; currentPoint = &tempCurrentPoint; nextPoint = &ptlist[(currentIndex+1)%numpts]; } else if ( nextPoint == ptlist ) { currentPoint = NULL; } else { currentPoint = nextPoint; currentIndex++; nextPoint = &ptlist[(currentIndex+1)%numpts]; } } // Selection sort the segments. Probably should replace this later. for (i=0; iclipReg ) { // Draw the lines for (y=minY; y<=maxY; y++) { i = 0; while (i y) break; if (segmentArray[i].MaxY() < y) { i++; continue; } if (segmentArray[i].MinY() == segmentArray[i].MaxY()) { if ( (segmentArray[i].MinX() < fDisplayMode.virtual_width) && (segmentArray[i].MaxX() >= 0) ) StrokePatternLine(ROUND(segmentArray[i].MinX()), y, ROUND(segmentArray[i].MaxX()), y, d); i++; } else { if ( (segmentArray[i+1].GetX(y) < fDisplayMode.virtual_width) && (segmentArray[i].GetX(y) >= 0) ) StrokePatternLine(ROUND(segmentArray[i].GetX(y)), y, ROUND(segmentArray[i+1].GetX(y)), y, d); i+=2; } } } } else { int numRects, rectIndex; int yStart, yEnd; BRect clipRect; numRects = d->clipReg->CountRects(); for (rectIndex = 0; rectIndex < numRects; rectIndex++) { clipRect = d->clipReg->RectAt(rectIndex); if ( clipRect.bottom < minY ) continue; if ( clipRect.top > maxY ) continue; if ( clipRect.left < minX ) continue; if ( clipRect.right > maxX ) continue; yStart = MAX(minY,ROUND(clipRect.top)); yEnd = MIN(maxY,ROUND(clipRect.bottom)); // Draw the lines for (y=yStart; y<=yEnd; y++) { i = 0; while (i y) break; if (segmentArray[i].MaxY() < y) { i++; continue; } if (segmentArray[i].MinY() == segmentArray[i].MaxY()) { if (segmentArray[i].MinX() > clipRect.right) { i++; continue; } if (segmentArray[i].MaxX() < clipRect.left) { i++; continue; } StrokePatternLine(ROUND(MAX(segmentArray[i].MinX(),clipRect.left)), y, ROUND(MIN(segmentArray[i].MaxX(),clipRect.right)), y, d); i++; } else { if (segmentArray[i].GetX(y) > clipRect.right) { i+=2; continue; } if (segmentArray[i+1].GetX(y) < clipRect.left) { i+=2; continue; } StrokePatternLine(ROUND(MAX(segmentArray[i].GetX(y),clipRect.left)), y, ROUND(MIN(segmentArray[i+1].GetX(y),clipRect.right)), y, d); i+=2; } } } } } delete[] segmentArray; fCursorHandler.DriverShow(); Invalidate(bounds); Unlock(); } /*! \brief Called for all BView::FillRect calls \param r BRect to be filled. Guaranteed to be in the frame buffer's coordinate space \param color The color used to fill the rectangle */ void DisplayDriver::FillRect(const BRect &r, const RGBColor &color) { Lock(); if(fCursorHandler.IntersectsCursor(r)) fCursorHandler.DriverHide(); FillSolidRect(r,color); fCursorHandler.DriverShow(); Unlock(); } /*! \brief Called for all BView::FillRect calls \param r BRect to be filled. Guaranteed to be in the frame buffer's coordinate space \param pattern The pattern used to fill the rectangle \param high_color The high color of the pattern \param low_color The low color of the pattern */ void DisplayDriver::FillRect(const BRect &r, const DrawData *d) { if(!d) return; Lock(); if(fCursorHandler.IntersectsCursor(r)) fCursorHandler.DriverHide(); if ( d->clipReg ) { if ( d->clipReg->Intersects(r) ) { BRegion reg(r); reg.IntersectWith(d->clipReg); int numRects = reg.CountRects(); for(int32 i=0; iclipReg ) { BRegion drawReg = r; drawReg.IntersectWith(d->clipReg); numRects = drawReg.CountRects(); for(int32 i=0; iclipReg ) { int numRects, rectIndex; BRect clipRect; int left, right, y1, y2; int rectTop, rectBottom, rectLeft, rectRight; numRects = d->clipReg->CountRects(); for (rectIndex=0; rectIndexclipReg->RectAt(rectIndex); rectTop = ROUND(clipRect.top); rectBottom = ROUND(clipRect.bottom); rectLeft = ROUND(clipRect.left); rectRight = ROUND(clipRect.right); for (i=0; i<=(int)yrad; i++) { arc_x = xrad*sqrt(1-i*i/yrad2); left = ROUND(r.left + xrad - arc_x); right = ROUND(r.right - xrad + arc_x); if ( (left > rectRight) || (right < rectLeft) ) continue; y1 = ROUND(r.top + yrad - i); y2 = ROUND(r.bottom - yrad + i); if ( (y1 >= rectTop) && (y1 <= rectBottom) ) StrokePatternLine(MAX(left,rectLeft), y1, MIN(right,rectRight), y1,d); if ( (y2 >= rectTop) && (y2 <= rectBottom) ) StrokePatternLine(MAX(left,rectLeft), y2, MIN(right,rectRight), y2,d); } } } else { for (i=0; i<=(int)yrad; i++) { arc_x = xrad*sqrt(1-i*i/yrad2); StrokePatternLine(ROUND(r.left+xrad-arc_x), ROUND(r.top+yrad-i), ROUND(r.right-xrad+arc_x), ROUND(r.top+yrad-i),d); StrokePatternLine(ROUND(r.left+xrad-arc_x), ROUND(r.bottom-yrad+i), ROUND(r.right-xrad+arc_x), ROUND(r.bottom-yrad+i),d); } } FillPatternRect(BRect(r.left,r.top+yrad,r.right,r.bottom-yrad),d); fCursorHandler.DriverShow(); Invalidate(r); Unlock(); } void DisplayDriver::FillShape(const BRect &bounds, const int32 &opcount, const int32 *oplist, const int32 &ptcount, const BPoint *ptlist, const DrawData *d) { // TODO: Implement DisplayDriver::FillShape. How, though? AGG backend? printf("DisplayDriver::FillShape unimplemented\n"); } void DisplayDriver::FillTriangle(BPoint *pts, const BRect &bounds, const RGBColor &color) { if ( !pts ) return; Lock(); if(fCursorHandler.IntersectsCursor(bounds)) fCursorHandler.DriverHide(); BPoint first, second, third; // Sort points according to their y values and x values (y is primary) if ( (pts[0].y < pts[1].y) || ((pts[0].y == pts[1].y) && (pts[0].x <= pts[1].x)) ) { first=pts[0]; second=pts[1]; } else { first=pts[1]; second=pts[0]; } if ( (second.yclipReg ) { // For now, cop out and use FillPolygon // Need to investigate if Triangle specific code would save processing time FillPolygon(pts,3,bounds,d); fCursorHandler.DriverShow(); } else { BPoint first, second, third; // Sort points according to their y values and x values (y is primary) if ( (pts[0].y < pts[1].y) || ((pts[0].y == pts[1].y) && (pts[0].x <= pts[1].x)) ) { first=pts[0]; second=pts[1]; } else { first=pts[1]; second=pts[0]; } if ( (second.y