#include #include "renderer/common/Surface.h" #include "renderer/common/SimpleSurface.h" #ifndef M_PI #define M_PI 3.14159265358979323846 #endif namespace lime { class HardwareBuilder { public: HardwareBuilder(const GraphicsJob &inJob,const GraphicsPath &inPath,HardwareData &ioData, HardwareContext &inHardware) { mTexture = 0; bool tile_mode = false; mElement.mColour = 0xffffffff; mSolidMode = false; mPerpLen = 0.5; bool tessellate_lines = true; if (inJob.mIsTileJob) { mElement.mBitmapRepeat = true; mElement.mBitmapSmooth = false; mElement.mPrimType = ptTriangles; mElement.mScaleMode = ssmNormal; mElement.mWidth = -1; GraphicsBitmapFill *bmp = inJob.mFill->AsBitmapFill(); mSurface = bmp->bitmapData->IncRef(); mTexture = mSurface->GetOrCreateTexture(inHardware); mElement.mBitmapRepeat = false; mElement.mBitmapSmooth = bmp->smooth; tile_mode = true; } else if (inJob.mFill) { mSolidMode = true; mElement.mPrimType = inJob.mTriangles ? ptTriangles : ptTriangleFan; mElement.mScaleMode = ssmNormal; mElement.mWidth = -1; if (!SetFill(inJob.mFill,inHardware)) return; } else if (tessellate_lines && inJob.mStroke->scaleMode==ssmNormal) { // ptTriangleStrip? mElement.mPrimType = ptTriangles; GraphicsStroke *stroke = inJob.mStroke; if (!SetFill(stroke->fill,inHardware)) return; mPerpLen = stroke->thickness * 0.5; if (mPerpLen<=0.0) mPerpLen = 0.5; else if (mPerpLen<0.5) { mPerpLen = 0.5; } mCaps = stroke->caps; mJoints = stroke->joints; if (mJoints==sjMiter) mMiterLimit = stroke->miterLimit*mPerpLen; } else { tessellate_lines = false; mElement.mPrimType = ptLineStrip; GraphicsStroke *stroke = inJob.mStroke; mElement.mScaleMode = stroke->scaleMode; mElement.mWidth = stroke->thickness; SetFill(stroke->fill,inHardware); } mElement.mFirst = 0; mElement.mCount = 0; if (inJob.mTriangles) { bool has_colour = inJob.mTriangles->mColours.size()>0; unsigned int flags = 0; if (inJob.mTriangles->mType == vtVertexUVT) flags |= HardwareArrays::PERSPECTIVE; if (inJob.mTriangles->mBlendMode==bmAdd) flags |= HardwareArrays::BM_ADD; if (inJob.mTriangles->mBlendMode==bmMultiply) flags |= HardwareArrays::BM_MULTIPLY; if (inJob.mTriangles->mBlendMode==bmScreen) flags |= HardwareArrays::BM_SCREEN; if (mSurface && (mSurface->GetAlphaMode () == amPremultiplied)) flags |= HardwareArrays::AM_PREMULTIPLIED; mArrays = &ioData.GetArrays(mSurface,has_colour,flags); AddTriangles(inJob.mTriangles); if (inJob.mStroke && inJob.mStroke->fill) { mElement.mPrimType = ptLines; GraphicsStroke *stroke = inJob.mStroke; if (!SetFill(stroke->fill,inHardware)) return; mArrays = &ioData.GetArrays(mSurface,false,mGradFlags); mElement.mFirst = 0; mElement.mCount = 0; mElement.mScaleMode = ssmNormal; mElement.mWidth = stroke->thickness; AddTriangleLines(inJob.mTriangles); } } else if (tile_mode) { bool has_colour = false; BlendMode bm = bmNormal; GetTileFlags(&inPath.commands[inJob.mCommand0], inJob.mCommandCount, has_colour, bm); mArrays = &ioData.GetArrays(mSurface,has_colour,(bm == bmAdd) ? HardwareArrays::BM_ADD : (bm == bmMultiply) ? HardwareArrays::BM_MULTIPLY : (bm == bmScreen) ? HardwareArrays::BM_SCREEN : 0); AddTiles(&inPath.commands[inJob.mCommand0], inJob.mCommandCount, &inPath.data[inJob.mData0]); } else if (tessellate_lines && !mSolidMode) { mArrays = &ioData.GetArrays(mSurface,false,(mSurface && (mSurface->GetAlphaMode() == amPremultiplied)) ? mGradFlags | HardwareArrays::AM_PREMULTIPLIED : mGradFlags); AddLineTriangles(&inPath.commands[inJob.mCommand0], inJob.mCommandCount, &inPath.data[inJob.mData0]); } else { mArrays = &ioData.GetArrays(mSurface,false,(mSurface && (mSurface->GetAlphaMode() == amPremultiplied)) ? mGradFlags | HardwareArrays::AM_PREMULTIPLIED : mGradFlags); AddObject(&inPath.commands[inJob.mCommand0], inJob.mCommandCount, &inPath.data[inJob.mData0]); } } bool SetFill(IGraphicsFill *inFill,HardwareContext &inHardware) { mSurface = 0; mElement.mBitmapRepeat = true; mElement.mBitmapSmooth = false; mGradFlags = 0; GraphicsSolidFill *solid = inFill->AsSolidFill(); if (solid) { //if (solid -> mRGB.a == 0) //return false; mElement.mColour = solid->mRGB.ToInt(); } else { GraphicsGradientFill *grad = inFill->AsGradientFill(); if (grad) { mGradReflect = grad->spreadMethod == smReflect; int w = mGradReflect ? 512 : 256; mSurface = new SimpleSurface(w,1,pfARGB); mSurface->IncRef(); grad->FillArray( (ARGB *)mSurface->GetBase(), false); mElement.mBitmapRepeat = grad->spreadMethod!=smPad; mElement.mBitmapSmooth = true; mTextureMapper = grad->matrix.Inverse(); if (!grad->isLinear) { mGradFlags |= HardwareArrays::RADIAL; if (grad->focalPointRatio!=0) { int r = fabs(grad->focalPointRatio)*256.0; if (r>255) r = 255; mGradFlags |= (r << 8); if (grad->focalPointRatio<0) mGradFlags |= HardwareArrays::FOCAL_SIGN; } } //return true; } else { GraphicsBitmapFill *bmp = inFill->AsBitmapFill(); mTextureMapper = bmp->matrix.Inverse(); mSurface = bmp->bitmapData->IncRef(); mTexture = mSurface->GetOrCreateTexture(inHardware); mElement.mBitmapRepeat = bmp->repeat; mElement.mBitmapSmooth = bmp->smooth; } } //return false; return true; } ~HardwareBuilder() { if (mSurface) mSurface->DecRef(); } void CalcTexCoords() { Vertices &vertices = mArrays->mVertices; Vertices &tex = mArrays->mTexCoords; int v0 = vertices.size(); int t0 = tex.size(); tex.resize(v0); bool radial = mGradFlags & HardwareArrays::RADIAL; for(int i=t0;iPixelToTex(p); } else { // The point will be in the (-819.2 ... 819.2) range... if (radial) { p.x = (p.x +819.2) / 819.2 - 1.0; p.y = (p.y +819.2) / 819.2 - 1.0; if (mGradReflect) { p.x *= 0.5; p.y *= 0.5; } } else { p.x = (p.x +819.2) / 1638.4; p.y = 0; if (mGradReflect) p.x *= 0.5; } } tex[i] = p; } } void AddTriangles(GraphicsTrianglePath *inPath) { Vertices &vertices = mArrays->mVertices; Colours &colours = mArrays->mColours; Vertices &tex = mArrays->mTexCoords; DrawElements &elements = mArrays->mElements; bool persp = inPath->mType == vtVertexUVT; mElement.mFirst = vertices.size() / (persp?2:1); mElement.mPrimType = ptTriangles; //Just overwriting viewport mArrays->mViewport = inPath->mViewport; const float *t = &inPath->mUVT[0]; for(int v=0;vmVertices.size();v++) { if (!persp) { vertices.push_back(inPath->mVertices[v]); if(inPath->mColours.size()>0) { colours.push_back(inPath->mColours[v]);/*mwb*/ } } if (inPath->mType != vtVertex) { tex.push_back( mTexture->TexToPaddedTex( UserPoint(t[0],t[1]) ) ); t+=2; if (persp) { float w= 1.0/ *t++; vertices.push_back(inPath->mVertices[v]*w); vertices.push_back( UserPoint(0,w) ); } } } mElement.mCount = (vertices.size() - mElement.mFirst)/(persp ? 2:1); elements.push_back(mElement); } void AddTriangleLines(GraphicsTrianglePath *inPath) { Vertices &vertices = mArrays->mVertices; DrawElements &elements = mArrays->mElements; mElement.mFirst = vertices.size(); mElement.mPrimType = ptLines; //Just overwriting blend mode and viewport mArrays->mViewport = inPath->mViewport; int tri_count = inPath->mVertices.size()/3; UserPoint *tri = &inPath->mVertices[0]; for(int v=0;vmVertices; Vertices &tex = mArrays->mTexCoords; Colours &colours = mArrays->mColours; const UserPoint *point = (const UserPoint *)inData; mElement.mFirst = vertices.size(); vertices.reserve(vertices.size() + (6 * inCount)); tex.reserve(tex.size() + (6 * inCount)); colours.reserve(colours.size() + (6 * inCount)); for(int i=0;iPixelToTex(tex_pos); const UserPoint tp2 = mTexture->PixelToTex(UserPoint(tex_pos.x+size.x,tex_pos.y+size.y)); tex.push_back( tp1 ); tex.push_back( mTexture->PixelToTex(UserPoint(tex_pos.x+size.x,tex_pos.y)) ); tex.push_back( tp2 ); tex.push_back( tp1 ); tex.push_back( tp2 ); tex.push_back( mTexture->PixelToTex(UserPoint(tex_pos.x,tex_pos.y+size.y)) ); if (inCommands[i]&pcTile_Col_Bit) { const UserPoint &rg = *point++; const UserPoint &ba = *point++; #ifdef BLACKBERRY const uint32 col = ((int)(rg.x*255)) | (((int)(rg.y*255))<<8) | (((int)(ba.x*255))<<16) | (((int)(ba.y*255))<<24); #else const uint32 col = ((rg.x<0 ? 0 : rg.x>1?255 : (int)(rg.x*255))) | ((rg.y<0 ? 0 : rg.y>1?255 : (int)(rg.y*255))<<8) | ((ba.x<0 ? 0 : ba.x>1?255 : (int)(ba.x*255))<<16) | ((ba.y<0 ? 0 : ba.y>1?255 : (int)(ba.y*255))<<24); #endif colours.push_back( col ); colours.push_back( col ); colours.push_back( col ); colours.push_back( col ); colours.push_back( col ); colours.push_back( col ); } } } } mElement.mCount = vertices.size() - mElement.mFirst; if (mElement.mCount>0) mArrays->mElements.push_back(mElement); } #define FLAT 0.000001 void AddPolygon(Vertices &inOutline,const QuickVec &inSubPolys) { if (mSolidMode && inOutline.size()<3) return; Vertices &vertices = mArrays->mVertices; mElement.mFirst = vertices.size(); bool isConvex = inSubPolys.size()==1; if (mSolidMode) { if (isConvex) { UserPoint base = inOutline[0]; int last = inOutline.size()-2; int i = 0; bool positive = true; for( ;iFLAT) { positive = diff > 0; break; } } for(++i;iFLAT && (diff>0)!=positive) { isConvex = false; break; } } } if (!isConvex) ConvertOutlineToTriangles(inOutline,inSubPolys); } mElement.mCount = inOutline.size(); vertices.resize(mElement.mFirst + mElement.mCount); for(int i=0;imElements.push_back(mElement); if (!isConvex) mArrays->mElements.last().mPrimType = ptTriangles; } void AddObject(const uint8* inCommands, int inCount, const float *inData) { UserPoint *point = (UserPoint *)inData; UserPoint last_move; UserPoint last_point; int points = 0; QuickVec sub_poly_start; Vertices outline; for(int i=0;i0) { point++; continue; } // fallthrough case pcMoveTo: if (points>1) { // Move in the middle of a solid polygon - treat like a line... if (mSolidMode) { sub_poly_start.push_back(outline.size()); outline.push_back(*point); last_point = *point++; points++; break; } sub_poly_start.push_back(outline.size()); AddPolygon(outline,sub_poly_start); } else if (points==1 && last_move==*point) { point++; continue; } outline.resize(0); sub_poly_start.resize(0); points = 1; last_point = *point++; last_move = last_point; if (outline.empty()||outline.last()!=last_move) outline.push_back(last_move); break; case pcLineTo: if (points>0) { if (outline.empty() || outline.last()!=*point) outline.push_back(*point); last_point = *point++; points++; } break; case pcCurveTo: { double len = ((last_point-point[0]).Norm() + (point[1]-point[0]).Norm()) * 0.25; if (len==0) break; int steps = (int)len; if (steps<3) steps = 3; if (steps>100) steps = 100; double step = 1.0/(steps+1); double t = 0; for(int s=0;smMiterLimit) { UserPoint corner0 = p0+dir1*mMiterLimit; UserPoint corner1 = p1-dir2*mMiterLimit; vertices.push_back(inP); vertices.push_back(p0); vertices.push_back(corner0); vertices.push_back(inP); vertices.push_back(corner0); vertices.push_back(corner1); vertices.push_back(inP); vertices.push_back(corner1); vertices.push_back(p1); } else { UserPoint corner = p0+dir1*inAlpha; vertices.push_back(inP); vertices.push_back(p0); vertices.push_back(corner); vertices.push_back(inP); vertices.push_back(corner); vertices.push_back(p1); } } void AddCurveSegment(Vertices &vertices,UserPoint inP0,UserPoint inP1,UserPoint inP2, UserPoint perp0, UserPoint perp1, UserPoint p0_left, UserPoint p0_right, UserPoint p1_left, UserPoint p1_right) { double len = (inP0 - inP1).Norm() + (inP2 - inP1).Norm(); int steps = (int)len*0.1; if (len < 50 && steps < 10) steps = 50; else if (steps < 1) steps = 1; else if (steps > 100) steps = 100; double step = 1.0 / (steps); double t = 0; UserPoint v0 = p0_right - p0_left; UserPoint v1 = p1_right - p1_left; UserPoint last_p_left = inP0 - perp0; UserPoint last_p_right = inP0 + perp0; // Clip against end ... if ( v0.Cross(last_p_left-p0_left)>0 ) last_p_left = p0_left; if ( v0.Cross(last_p_right-p0_left)>0 ) last_p_right = p0_right; // TODO - against other end? for (int s=1; s <= steps; s++) { t += step; double t_ = 1.0 - t; UserPoint p = inP0 * (t_ * t_) + inP1 * (2.0 * t * t_) + inP2 * (t * t); UserPoint dir = (inP0 * -t_ + inP1 * (1.0 - 2.0 * t) + inP2 * t); UserPoint perp = dir.Perp(mPerpLen); if (s==step) { p = inP2; perp = perp1; } UserPoint p_right = p + perp; UserPoint p_left = p - perp; if (v0.Cross(dir) < 0 ) // if pointing in same direction (left-handed) { if ( v0.Cross(p_left-p0_left)>0) p_left = p0_left; if ( v0.Cross(p_right-p0_left)>0) p_right = p0_right; } if (v1.Cross(dir) < 0 ) // if pointing in same direction (left-handed) { if ( v1.Cross(p_left-p1_left)<0) p_left = p1_left; if ( v1.Cross(p_right-p1_left)<0) p_right = p1_right; } if (p_left==last_p_left) { if (p_right!=last_p_right) { vertices.push_back(p_left); vertices.push_back(last_p_right); vertices.push_back(p_right); } } else if (p_right==last_p_right) { if (p_left!=last_p_left) { vertices.push_back(last_p_left); vertices.push_back(p_right); vertices.push_back(p_left); } } else { vertices.push_back(last_p_left); vertices.push_back(last_p_right); vertices.push_back(p_left); vertices.push_back(p_left); vertices.push_back(last_p_right); vertices.push_back(p_right); } last_p_left = p_left; last_p_right = p_right; } } void EndCap(Vertices &vertices,UserPoint p0, UserPoint perp) { UserPoint back(-perp.y, perp.x); if (mCaps==scSquare) { vertices.push_back(p0+perp); vertices.push_back(p0+perp + back); vertices.push_back(p0-perp); vertices.push_back(p0+perp + back); vertices.push_back(p0-perp + back); vertices.push_back(p0-perp); } else { int n = std::max(2,(int)(mPerpLen * 4)); double dtheta = M_PI / n; double theta = 0.0; UserPoint prev(perp); for(int i=1;i &inPath, bool inLoop) { Vertices &vertices = mArrays->mVertices; mElement.mFirst = vertices.size(); // Endcap 0 ... if (!inLoop && (mCaps==scSquare || mCaps==scRound)) { UserPoint p0 = inPath[0].p; EndCap(vertices, p0, inPath[1].getDir0(p0).Perp(mPerpLen)); } double prev_alpha = 0; for(int i=1;i next_perp-perp1 = alpha*(dir1+next_dir) -> alpha = prep1-next_perp in either x or y direction... --------------- dir1+next_dir Make the split such that DpY and down belongs to this segment - first add this DpY triangle, then the remainder of the segment happens below the D-Y line. On one side (right hand, drawn here) the segments will overlap - on the other side, there will be a join. Which side this will be depends on the sign of alpha. When reversed, the roles of Y and Z will change. BpY is for this upper segment - but there is an equivalent B'p0Y' for this segment. First we add B'p-Y' triangle, and work from B'Y' line */ UserPoint perp0(-dir0.y*mPerpLen, dir0.x*mPerpLen); UserPoint perp1(-dir1.y*mPerpLen, dir1.x*mPerpLen); UserPoint next_perp(-next_dir.y*mPerpLen, next_dir.x*mPerpLen); double denom_x = dir1.x+next_dir.x; double denom_y = dir1.y+next_dir.y; double alpha=0; // Choose the better-conditioned axis if (fabs(denom_x)>fabs(denom_y)) alpha = denom_x==0 ? 0 : (perp1.x-next_perp.x)/denom_x; else alpha = denom_y==0 ? 0 : (perp1.y-next_perp.y)/denom_y; UserPoint p1_left = p-perp1; UserPoint p1_right = p+perp1; // This could start getting dodgy when the line doubles-back double max_alpha = std::max( (p0-p).Norm()*0.5, mPerpLen ); if (fabs(alpha)>max_alpha) { // draw overlapped } else if (alpha>0) { p1_right -= dir1 * alpha; vertices.push_back(p1_left); vertices.push_back(p); vertices.push_back(p1_right); } else if (alpha<0) { p1_left += dir1 * alpha; vertices.push_back(p1_left); vertices.push_back(p); vertices.push_back(p1_right); } UserPoint p0_left = p0-perp0; UserPoint p0_right = p0+perp0; if (i==1 && inLoop) { UserPoint prev_dir = inPath[inPath.size()-1].getDir1(inPath[inPath.size()-2].p).Normalized(); UserPoint prev_perp(-prev_dir.y*mPerpLen, prev_dir.x*mPerpLen); double denom_x = prev_dir.x+dir0.x; double denom_y = prev_dir.y+dir0.y; // Choose the better-conditioned axis if (fabs(denom_x)>fabs(denom_y)) prev_alpha = denom_x==0 ? 0 : (prev_perp.x-perp0.x)/denom_x; else prev_alpha = denom_y==0 ? 0 : (prev_perp.y-perp0.y)/denom_y; } if (fabs(prev_alpha)>max_alpha) { // do nothing } else if (prev_alpha>0) { p0_right += dir0 * std::min(prev_alpha,max_alpha); vertices.push_back(p0_left); vertices.push_back(p0); vertices.push_back(p0_right); } else if (prev_alpha<0) { p0_left -= dir0 * std::max(prev_alpha,-max_alpha); vertices.push_back(p0_left); vertices.push_back(p0); vertices.push_back(p0_right); } if (alpha) switch(mPerpLen<1 ? sjBevel : mJoints) { case sjRound: { double angle = acos(dir1.Dot(next_dir)); if (angle<0) angle += M_PI; if (alpha>0) // left AddArc(vertices, p, angle, -perp1, dir1*mPerpLen, p1_left, p-next_perp ); else // right AddArc(vertices, p, angle, perp1, dir1*mPerpLen, p1_right, p+next_perp ); } break; case sjMiter: if (alpha>0) // left AddMiter(vertices, p, p-perp1, p-next_perp, alpha, dir1, next_dir); else // Right AddMiter(vertices, p, p+perp1, p+next_perp, -alpha, dir1, next_dir); break; case sjBevel: if (alpha>0) // left { vertices.push_back(p1_left); vertices.push_back(p); vertices.push_back(p-next_perp); } else { vertices.push_back(p1_right); vertices.push_back(p); vertices.push_back(p+next_perp); } break; } if (seg.isCurve()) { AddCurveSegment(vertices,p0,seg.curve,seg.p, perp0, perp1, p0_left, p0_right, p1_left, p1_right); } else { vertices.push_back(p0_left); vertices.push_back(p0_right); vertices.push_back(p1_right); vertices.push_back(p0_left); vertices.push_back(p1_right); vertices.push_back(p1_left); } // Endcap end ... if (!inLoop && (i+1==inPath.size()) && (mCaps==scSquare || mCaps==scRound)) EndCap(vertices, p, dir1.Perp(-mPerpLen)); prev_alpha = alpha; } mElement.mCount = vertices.size()-mElement.mFirst; if (mSurface) CalcTexCoords(); mArrays->mElements.push_back(mElement); } void AddLineTriangles(const uint8* inCommands, int inCount, const float *inData) { UserPoint *point = (UserPoint *)inData; // It is a loop if the path has no breaks, it has more than 2 points // and it finishes where it starts... UserPoint first; UserPoint prev; QuickVec strip; for(int i=0;i1) AddStrip(strip,false); strip.resize(0); strip.push_back(Segment(*point)); prev = *point; first = *point++; break; case pcWideLineTo: point++; case pcLineTo: { if (strip.size()>0 && *point==prev) { point++; continue; } strip.push_back(Segment(*point)); // Implicit loop closing... if (strip.size()>2 && *point==first) { AddStrip(strip,true); strip.resize(0); first = *point; } prev = *point; point++; } break; case pcCurveTo: { if (strip.size()>0 && *point==prev && point[1]==prev) { point+=2; continue; } strip.push_back(Segment(point[1],point[0])); // Implicit loop closing... if (strip.size()>2 && point[1]==first) { AddStrip(strip,true); strip.resize(0); first = point[1]; } prev = point[1]; point +=2; } break; case pcTile: point+=3; break; case pcTileTrans: point+=4; break; case pcTileCol: point+=5; break; case pcTileTransCol: point+=6; break; } } if (strip.size()>0) AddStrip(strip,false); } HardwareArrays *mArrays; Surface *mSurface; DrawElement mElement; Texture *mTexture; bool mGradReflect; unsigned int mGradFlags; bool mSolidMode; double mMiterLimit; double mPerpLen; Matrix mTextureMapper; StrokeCaps mCaps; StrokeJoints mJoints; }; void CreatePointJob(const GraphicsJob &inJob,const GraphicsPath &inPath,HardwareData &ioData, HardwareContext &inHardware) { DrawElement elem; elem.mColour = 0xffffffff; GraphicsSolidFill *fill = inJob.mFill ? inJob.mFill->AsSolidFill() : 0; if (fill) elem.mColour = fill->mRGB.ToInt(); GraphicsStroke *stroke = inJob.mStroke; if (stroke) { elem.mScaleMode = stroke->scaleMode; elem.mWidth = stroke->thickness; } else { elem.mScaleMode = ssmNormal; elem.mWidth = -1; } elem.mPrimType = ptPoints; elem.mCount = inJob.mDataCount / (fill ? 2 : 3); HardwareArrays *arrays = &ioData.GetArrays(0,fill==0, /* TODO: bm add ? */ 0); Vertices &vertices = arrays->mVertices; elem.mFirst = vertices.size(); vertices.resize( elem.mFirst + elem.mCount ); memcpy( &vertices[elem.mFirst], &inPath.data[ inJob.mData0 ], elem.mCount*sizeof(UserPoint) ); if (!fill) { Colours &colours = arrays->mColours; colours.resize( elem.mFirst + elem.mCount ); const int * src = (const int *)(&inPath.data[ inJob.mData0 + elem.mCount*2]); int * dest = &colours[elem.mFirst]; int n = elem.mCount; for(int i=0;i>16)&0xff) | ((s<<16) & 0xff0000); } } arrays->mElements.push_back(elem); } void BuildHardwareJob(const GraphicsJob &inJob,const GraphicsPath &inPath,HardwareData &ioData, HardwareContext &inHardware) { if (inJob.mIsPointJob) CreatePointJob(inJob,inPath,ioData,inHardware); else { HardwareBuilder builder(inJob,inPath,ioData,inHardware); } } // --- HardwareArrays --------------------------------------------------------------------- HardwareArrays::HardwareArrays(Surface *inSurface,unsigned int inFlags) { mFlags = inFlags; mSurface = inSurface; if (inSurface) inSurface->IncRef(); } HardwareArrays::~HardwareArrays() { if (mSurface) mSurface->DecRef(); } bool HardwareArrays::ColourMatch(bool inWantColour) { if (mVertices.empty()) return true; return mColours.empty() != inWantColour; } // --- HardwareData --------------------------------------------------------------------- HardwareData::~HardwareData() { mCalls.DeleteAll(); } HardwareArrays &HardwareData::GetArrays(Surface *inSurface,bool inWithColour,unsigned int inFlags) { if (mCalls.empty() || mCalls.last()->mSurface != inSurface || !mCalls.last()->ColourMatch(inWithColour) || mCalls.last()->mFlags != inFlags ) { HardwareArrays *arrays = new HardwareArrays(inSurface,inFlags); mCalls.push_back(arrays); } return *mCalls.last(); } // --- Texture ----------------------------- void Texture::Dirty(const Rect &inRect) { if (!mDirtyRect.HasPixels()) mDirtyRect = inRect; else mDirtyRect = mDirtyRect.Union(inRect); } // --- HardwareContext ----------------------------- // Cache line thickness transforms... static Matrix sLastMatrix; double sLineScaleV = -1; double sLineScaleH = -1; double sLineScaleNormal = -1; bool HardwareContext::Hits(const RenderState &inState, const HardwareCalls &inCalls ) { if (inState.mClipRect.w!=1 || inState.mClipRect.h!=1) return false; UserPoint screen(inState.mClipRect.x, inState.mClipRect.y); UserPoint pos = inState.mTransform.mMatrix->ApplyInverse(screen); if (sLastMatrix!=*inState.mTransform.mMatrix) { sLastMatrix=*inState.mTransform.mMatrix; sLineScaleV = -1; sLineScaleH = -1; sLineScaleNormal = -1; } for(int c=0;cx1) prev |= 0x02; if (p0.yy1) prev |= 0x08; if (prev==0 && pos.Dist2(p0)<=w2) return true; for(int i=1;ix1) flags |= 0x02; if (p.yy1) flags |= 0x08; if (flags==0 && pos.Dist2(p)<=w2) return true; if ( !(flags & prev) ) { // Line *may* pass though the point... UserPoint vec = p-p0; double len = sqrt(vec.x*vec.x + vec.y*vec.y); if (len>0) { double a = vec.Dot(pos-p0)/len; if (a>0 && a<1) { if ( (p0 + vec*a).Dist2(pos) < w2 ) return true; } } } prev = flags; p0 = p; } } else if (draw.mPrimType == ptTriangleFan) { if (draw.mCount<3) continue; UserPoint *v = &vert[ draw.mFirst ]; UserPoint p0 = *v; int count_left = 0; for(int i=1;i<=draw.mCount;i++) { UserPoint p = v[i%draw.mCount]; if ( (p.ybase.x; if ( bgx!=(pos.x>v[0].x) || bgx!=(pos.x>v[1].x) ) { bool bgy = pos.y>base.y; if ( bgy!=(pos.y>v[0].y) || bgy!=(pos.y>v[1].y) ) { UserPoint v0 = v[0] - base; UserPoint v1 = v[1] - base; UserPoint v2 = pos - base; double dot00 = v0.Dot(v0); double dot01 = v0.Dot(v1); double dot02 = v0.Dot(v2); double dot11 = v1.Dot(v1); double dot12 = v1.Dot(v2); // Compute barycentric coordinates double denom = (dot00 * dot11 - dot01 * dot01); if (denom!=0) { denom = 1 / denom; double u = (dot11 * dot02 - dot01 * dot12) * denom; if (u>=0) { double v = (dot00 * dot12 - dot01 * dot02) * denom; // Check if point is in triangle if ( (v >= 0) && (u + v < 1) ) return true; } } } } v+=2; } } } } return false; } } // end namespace lime