Ejemplo n.º 1
0
 RectShape(const GBitmap& bm, int x, int y) : Shape(x, y) {
     fRect.setXYWH(x, y, bm.width(), bm.height());
     fRect.offset(-fRect.centerX(), -fRect.centerY());
     fPaint.setRGB(gRand.nextF(), gRand.nextF(), gRand.nextF());
 }
Ejemplo n.º 2
0
void ClipMap::fillWithTestPattern()
{
   AssertISV(false, "ClipMap::fillWithTestPattern - assumes bitmaps, which "
      "are no longer present, switch to lock() semantics if you need this!");

   // Table of random colors.
   U8 colorTbl[16][3] = 
   {
      { 0xFF, 0x00, 0x0F },
      { 0xFF, 0x00, 0xA0 },
      { 0xFF, 0x00, 0xFF },
      { 0x00, 0xA0, 0x00 },
      { 0x00, 0xA0, 0xAF },
      { 0x00, 0xA0, 0xF0 },
      { 0xA0, 0xFF, 0xA0 },
      { 0x00, 0xF0, 0xA0 },
      { 0x00, 0xF0, 0xFF },
      { 0xA0, 0x00, 0x00 },
      { 0xA0, 0x00, 0xAF },
      { 0xA0, 0x00, 0xF0 },
      { 0xA0, 0xF0, 0x0F },
      { 0xA0, 0xF0, 0xA0 },
      { 0xA0, 0xF0, 0xFF },
      { 0x00, 0xFF, 0x00 },
   };

   // Lock each layer of each texture and write a test pattern in.
   
   // Base levels first.
   for(S32 i=0; i<mClipStackDepth; i++)
   {
      GTexHandle &gth = mLevels[i].mTex;
      U8 *bmpData = (U8*)gth.getBitmap()->getWritableBits(0);

      for(S32 x=0; x<gth.getHeight(); x++)
      {
         for(S32 y=0; y<gth.getWidth(); y++)
         {
            S32 xFlag = x & 4;
            S32 yFlag = y & 4;

            U32 offset = (x * gth.getWidth() + y) * 4;
            if(xFlag ^ yFlag)
            {
               // Set bright.
               bmpData[offset+0] = colorTbl[i][0];
               bmpData[offset+1] = colorTbl[i][1];
               bmpData[offset+2] = colorTbl[i][2];
               bmpData[offset+3] = 0xFF;
            }
            else
            {
               // Set dim.
               bmpData[offset+0] = colorTbl[i][0] / 3;
               bmpData[offset+1] = colorTbl[i][1] / 3;
               bmpData[offset+2] = colorTbl[i][2] / 3;
               bmpData[offset+3] = 0xFF;
            }
         }
      }

      if(i == mClipStackDepth - 1)
         gth.getBitmap()->extrudeMipLevels();
      else
      {
         // Write black/translucent in the higher levels.
         GBitmap *gb = gth.getBitmap();
         for(S32 j=1; j<gb->getNumMipLevels(); j++)
         {
            U8 *b = gb->getWritableBits(j);
            dMemset(b, 0, 4 * gb->getWidth(j) * gb->getHeight(j));
         }
      }

      gth.refresh();
   }
}
void 
JB2Dict::JB2Codec::Encode::code_absolute_mark_size(GBitmap &bm, int border)
{
  CodeNum(bm.columns(), 0, BIGPOSITIVE, abs_size_x);
  CodeNum(bm.rows(), 0, BIGPOSITIVE, abs_size_y);
}
void 
JB2Dict::JB2Codec::Encode::code_relative_mark_size(GBitmap &bm, int cw, int ch, int border)
{
  CodeNum(bm.columns()-cw, BIGNEGATIVE, BIGPOSITIVE, rel_size_x);
  CodeNum(bm.rows()-ch, BIGNEGATIVE, BIGPOSITIVE, rel_size_y);
}
Ejemplo n.º 5
0
Point2I GuiColorPickerCtrl::findColor(const ColorF & color, const Point2I& offset, const Point2I& resolution, GBitmap& bmp)
{
   RectI rect;
   Point2I ext = getExtent();
   if (mDisplayMode != pDropperBackground)
   {
      ext.x -= 3;
      ext.y -= 2;
      rect = RectI(Point2I(1, 1), ext);
   }
   else
   {
      rect = RectI(Point2I(0, 0), ext);
   }

   Point2I closestPos(-1, -1);

   /* Debugging
   char filename[256];
   dSprintf( filename, 256, "%s.%s", "colorPickerTest", "png" );

   // Open up the file on disk.
   FileStream fs;
   if ( !fs.open( filename, Torque::FS::File::Write ) )
   Con::errorf( "GuiObjectView::saveAsImage() - Failed to open output file '%s'!", filename );
   else
   {
   // Write it and close.
   bmp.writeBitmap( "png", fs );

   fs.close();
   }
   */

   ColorI tmp;
   U32 buf_x;
   U32 buf_y;
   ColorF curColor;
   F32 val(10000.0f);
   F32 closestVal(10000.0f);
   bool closestSet = false;

   for (S32 x = rect.point.x; x <= rect.extent.x; x++)
   {
      for (S32 y = rect.point.y; y <= rect.extent.y; y++)
      {
         buf_x = offset.x + x + 1;
         buf_y = (resolution.y - (offset.y + y + 1));
         buf_y = resolution.y - buf_y;

         //Get the color at that position
         bmp.getColor(buf_x, buf_y, tmp);
         curColor = (ColorF)tmp;

         //Evaluate how close the color is to our desired color
         val = mFabs(color.red - curColor.red) + mFabs(color.green - curColor.green) + mFabs(color.blue - curColor.blue);

         if (!closestSet)
         {
            closestVal = val;
            closestPos.set(x, y);
            closestSet = true;
         }
         else if (val < closestVal)
         {
            closestVal = val;
            closestPos.set(x, y);
         }
      }
   }

   return closestPos;
}
Ejemplo n.º 6
0
S32 GuiControlProfile::constructBitmapArray()
{
   if(mBitmapArrayRects.size())
      return mBitmapArrayRects.size();

   if( mTextureObject.isNull() )
   {   
      if ( !mBitmapName || !mBitmapName[0] || !mTextureObject.set( mBitmapName, &GFXDefaultPersistentProfile, avar("%s() - mTextureObject (line %d)", __FUNCTION__, __LINE__) ))
         return 0;
   }

   GBitmap *bmp = mTextureObject->getBitmap();

   //get the separator color
   ColorI sepColor;
   if ( !bmp || !bmp->getColor( 0, 0, sepColor ) )
	{
      Con::errorf("Failed to create bitmap array from %s for profile %s - couldn't ascertain seperator color!", mBitmapName, getName());
      AssertFatal( false, avar("Failed to create bitmap array from %s for profile %s - couldn't ascertain seperator color!", mBitmapName, getName()));
      return 0;
	}

   //now loop through all the scroll pieces, and find the bounding rectangle for each piece in each state
   S32 curY = 0;

   // ascertain the height of this row...
   ColorI color;
   mBitmapArrayRects.clear();
   while(curY < bmp->getHeight())
   {
      // skip any sep colors
      bmp->getColor( 0, curY, color);
      if(color == sepColor)
      {
         curY++;
         continue;
      }
      // ok, process left to right, grabbing bitmaps as we go...
      S32 curX = 0;
      while(curX < bmp->getWidth())
      {
         bmp->getColor(curX, curY, color);
         if(color == sepColor)
         {
            curX++;
            continue;
         }
         S32 startX = curX;
         while(curX < bmp->getWidth())
         {
            bmp->getColor(curX, curY, color);
            if(color == sepColor)
               break;
            curX++;
         }
         S32 stepY = curY;
         while(stepY < bmp->getHeight())
         {
            bmp->getColor(startX, stepY, color);
            if(color == sepColor)
               break;
            stepY++;
         }
         mBitmapArrayRects.push_back(RectI(startX, curY, curX - startX, stepY - curY));
      }
      // ok, now skip to the next separation color on column 0
      while(curY < bmp->getHeight())
      {
         bmp->getColor(0, curY, color);
         if(color == sepColor)
            break;
         curY++;
      }
   }
   return mBitmapArrayRects.size();
}
Ejemplo n.º 7
0
void blInteriorProxy::addToShadowVolume(ShadowVolumeBSP * shadowVolume, LightInfo * light, S32 level)
{
    if(light->getType() != LightInfo::Vector)
        return;

    ColorF ambient = light->getAmbient();

    bool shadowedTree = true;

    InteriorInstance* interior = dynamic_cast<InteriorInstance*>(getObject());
    if (!interior)
        return;
    Resource<InteriorResource> mInteriorRes = interior->getResource();

    // check if just getting shadow detail
    if(level == SceneLighting::SHADOW_DETAIL)
    {
        shadowedTree = false;
        level = mInteriorRes->getNumDetailLevels() - 1;
    }

    Interior * detail = mInteriorRes->getDetailLevel(level);
    bool hasAlarm = detail->hasAlarmState();

    // make sure surfaces do not get processed more than once
    BitVector surfaceProcessed;
    surfaceProcessed.setSize(detail->mSurfaces.size());
    surfaceProcessed.clear();

    ColorI color = light->getAmbient();

    // go through the zones of the interior and grab outside visible surfaces
    for(U32 i = 0; i < detail->getNumZones(); i++)
    {
        Interior::Zone & zone = detail->mZones[i];
        for(U32 j = 0; j < zone.surfaceCount; j++)
        {
            U32 surfaceIndex = detail->mZoneSurfaces[zone.surfaceStart + j];

            // dont reprocess a surface
            if(surfaceProcessed.test(surfaceIndex))
                continue;
            surfaceProcessed.set(surfaceIndex);

            Interior::Surface & surface = detail->mSurfaces[surfaceIndex];

            // outside visible?
            if(!(surface.surfaceFlags & Interior::SurfaceOutsideVisible))
                continue;

            // good surface?
            PlaneF plane = detail->getPlane(surface.planeIndex);
            if(Interior::planeIsFlipped(surface.planeIndex))
                plane.neg();

            // project the plane
            PlaneF projPlane;
            mTransformPlane(interior->getTransform(), interior->getScale(), plane, &projPlane);

            // fill with ambient? (need to do here, because surface will not be
            // added to the SVBSP tree)
            F32 dot = mDot(projPlane, light->getDirection());
            if(dot > -gParellelVectorThresh)// && !(GFX->getPixelShaderVersion() > 0.0) )
            {
                if(shadowedTree)
                {
                    // alarm lighting
                    GFXTexHandle normHandle = gInteriorLMManager.duplicateBaseLightmap(detail->getLMHandle(), interior->getLMHandle(), detail->getNormalLMapIndex(surfaceIndex));
                    GFXTexHandle alarmHandle;

                    GBitmap * normLightmap = normHandle->getBitmap();
                    GBitmap * alarmLightmap = 0;

                    // check if they share the lightmap
                    if(hasAlarm)
                    {
                        if(detail->getNormalLMapIndex(surfaceIndex) != detail->getAlarmLMapIndex(surfaceIndex))
                        {
                            alarmHandle = gInteriorLMManager.duplicateBaseLightmap(detail->getLMHandle(), interior->getLMHandle(), detail->getAlarmLMapIndex(surfaceIndex));
                            alarmLightmap = alarmHandle->getBitmap();
                        }
                    }

                    //
                    // Support for interior light map border sizes.
                    //
                    U32 xlen, ylen, xoff, yoff;
                    U32 lmborder = detail->getLightMapBorderSize();
                    xlen = surface.mapSizeX + (lmborder * 2);
                    ylen = surface.mapSizeY + (lmborder * 2);
                    xoff = surface.mapOffsetX - lmborder;
                    yoff = surface.mapOffsetY - lmborder;

                    // attemp to light normal and alarm lighting
                    for(U32 c = 0; c < 2; c++)
                    {
                        GBitmap * lightmap = (c == 0) ? normLightmap : alarmLightmap;
                        if(!lightmap)
                            continue;

                        // fill it
                        for(U32 y = 0; y < ylen; y++)
                        {
                            for(U32 x = 0; x < xlen; x++)
                            {
                                ColorI outColor(255, 0, 0, 255);

#ifndef SET_COLORS
                                ColorI lmColor(0, 0, 0, 255);
                                lightmap->getColor(xoff + x, yoff + y, lmColor);

                                U32 _r = static_cast<U32>( color.red ) + static_cast<U32>( lmColor.red );
                                U32 _g = static_cast<U32>( color.green ) + static_cast<U32>( lmColor.green );
                                U32 _b = static_cast<U32>( color.blue ) + static_cast<U32>( lmColor.blue );

                                outColor.red   = mClamp(_r, 0, 255);
                                outColor.green = mClamp(_g, 0, 255);
                                outColor.blue  = mClamp(_b, 0, 255);
#endif

                                lightmap->setColor(xoff + x, yoff + y, outColor);
                            }
                        }
                    }
                }
                continue;
            }

            ShadowVolumeBSP::SVPoly * poly = buildInteriorPoly(shadowVolume, detail,
                                             surfaceIndex, light, shadowedTree);

            // insert it into the SVBSP tree
            shadowVolume->insertPoly(poly);
        }
    }
}
Ejemplo n.º 8
0
void GBitsetOCR::textOCR_(vector<stringOCR>&correctionTable,GLogicProcessor *logicProcessor, int mode){
	int baseCount;
	unsigned int letterW,letterH;
	int print=0;
	int w,h,yLimit0,yLimit1,id_index;
	string letter;
	int indexW;
	mainString="";
	indexW=0; id_index=0;
	vector<wordOCR>lineText;
	vector<OCRMatch>matchLine;
	
	static float bestScale=0;
	static int bestCorrelation=0;
	int needLineSize=0;
	int lineCorrelation=0;
    float scalePage=(float)atoi(inputData.data["scale"].c_str())/100;  //1 не выполняется масштабирование
    if(scalePage<0)scalePage=0; if(scalePage>5)scalePage=5;  //cout<<"scale="<<scalePage<<endl;
    GBitmap *line;
    
#define  c_out_ cout

int draw=0;
if(inputData.data["ocrData"]=="drawLetter"){
	draw=1; print=0;

c_out_<<"<span style=\"font-family:OCRUnicode;\">";
c_out_<<"inputData.ocrData="<<inputData.data["ocrData"]<<END;
}
	
DT("//_________________________print text OCR"<<END);
DT("//_________________________start correlation"<<END);
if(strArray[0].size()==0)return;


	

	baseCount=0;  letterW=0; letterH=0;
		//setOCRCorrelation(0,1,ROOT_LETTER); return;
if(!aliKali)return;    
if(aliKali->letterCount()<1)return;

//int OCRMode=1; //- Tibetan print text
int OCRMode=2; //- Tibetan woodblock text

TIME_START
    
	//for (int index=15;index>=15; --index){  //for every string
	for (int indexLine=(int)strArray[0].size()-1;indexLine>=0; --indexLine){  //for every string
        
        if((inputData.data["ocrData"]=="testOCRLine")&&!strArray[0][indexLine].selectFlag){continue;}

        //if(index!=21)continue;
        
	    lineCorrelation=0; lineText.resize(0);
		//if(readFromCorrectionTable(correctionTable,pageText,strArray[0].size()-3-index))continue;  //read string from human prof reading pages.
        //##if(readFromCorrectionTable(correctionTable,pageText,index))continue;  //read string from human prof reading pages.
		    
						//Create GBitsetMatrix for matrix correlation
						//SH(inputData.borderSize());
						w=inputBitmap->columns();
						h=inputBitmap->rows();
		
		                //*** ВЫЧИСЛЕНИЕ ГАБАРИТОВ ПОИСКА БУКВ В СТРОКЕ ***///
		                   yLimit0=(strArray[0][indexLine].y1-strArray[0][indexLine].y0)*3;	
		                   yLimit1=(strArray[0][indexLine].y1-strArray[0][indexLine].y0)*4; 
		                   int LimY0=yLimit0*0.8;  
		                  //normalisation
		

							DT2("w="<<w<<"h="<<h<<
							   "y0="<< strArray[0][indexLine].y0<<" y1="<< strArray[0][indexLine].y1<<" yLimit0="<<yLimit0<<" yLimit1="<<yLimit1<<END);
		
		                   if(strArray[0][indexLine].y0-yLimit0<0)yLimit0=strArray[0][indexLine].y0;
						   if(strArray[0][indexLine].y1+yLimit1>h)yLimit1=h-strArray[0][indexLine].y1;
											
		                   DT2("w="<<w<<"h="<<h<<
						   "y0="<< strArray[0][indexLine].y0<<" y1="<< strArray[0][indexLine].y1<<" yLimit0="<<yLimit0<<" yLimit1="<<yLimit1<<END);
		                   DT2("@@Y0="<<strArray[0][indexLine].y0-yLimit0<<endl);
		                   DT2("@@H="<<strArray[0][indexLine].y1-strArray[0][indexLine].y0+yLimit0+yLimit1);
		                   
		                int textLineSize=strArray[0][indexLine].y1-strArray[0][indexLine].y0;
		      
						DT2(" textLineSize1="<<textLineSize);
		                float scale=0,k;   
		                int needScaleFlag=1; //1 если нужно масштабировать строку
		               
		if(scalePage!=1){
                                line=GBitmap::createRegion(inputBitmap, 
															0, 
															strArray[0][indexLine].y0-LimY0, 
															inputBitmap->columns(), 
															yLimit0*2.5
		                                                    );
			                    if(scalePage!=0)needScaleFlag=0; 
		}                
		                 int correlation; 
		                 GBitmap *lineScale;
       
       //pageTextSize0=pageText.size();   //размер текста до распознавания строки
	   
       ScaleDetector:;   DT("needScaleFlag="<<needScaleFlag<<" needLineSize="<<needLineSize<<endl);
       
       strArray[0][indexLine].clearOCR();
       lineText.resize(0);
        
       //pageTextSize1=pageText.size(); //размер текста до распознавания строки или после первого распознавания с выбранным масштабом (после возврата по GOTO)
	   
        if(scalePage==1){
           scale=1;
           needScaleFlag=0; 
           
        }

       if(needScaleFlag){ 
                        if(needLineSize<3){
                            scale=1;
							scale=lineOCRScaleDetector(line,
													   matchLine, 
													   LimY0*scale, 
													   (LimY0+textLineSize)*scale, 
													   scale,
													   indexLine,
													   &correlation,
													   1);
							if(scale){
								k=bestScale/scale; DT("bestScale="<<bestScale<<" scale="<<scale<<" k="<<k<<endl);
							    if(k>0.8&&k<1.2){needLineSize++;}else{needLineSize=0;}
							}else{needLineSize=0;}	
							bestScale=scale;
							bestCorrelation=correlation;
							
						}else{
						    scale=bestScale;
						}
							
		                if(scale==0){
							scale=1;  //no rezult from scale detector
                            //continue;
							//stepCount++;
							//if(stepCount>2){needLineSize=0;continue;}
							//goto_ScaleDetector;
			            }
           //cout<<"@3index="<<index<<" scalePage="<<scalePage<<" needScaleFlag="<<needScaleFlag<<endl;

	 DT("m0_1");    
                      
                        ///scale=1.74;
                       lineScale=GBitmap::createScale(line,scale);
                       setMatrix=GBitsetMatrix::createRegion(lineScale,
                                                             0,
                                                             0,
                                                             lineScale->columns(),
                                                             lineScale->rows(),
                                                             1,1,
                                                             IMGNOFLIP
                                                             );      
                       lineScale->destroy();
	}else {

                        if(scalePage!=1){ 
                            scale=scalePage;
                            lineScale=GBitmap::createScale(line,scale);
                            setMatrix=GBitsetMatrix::createRegion(lineScale,
                                                                  0,
                                                                  0,
                                                                  lineScale->columns(),
                                                                  lineScale->rows(),
                                                                  1,1,
                                                                  IMGNOFLIP
                                                                  );      
                            lineScale->destroy();                      
                        }else{
                        
                        setMatrix=GBitsetMatrix::createRegion(inputBitmap, 
                                                              0, 
                                                              strArray[0][indexLine].y0-LimY0, 
                                                              inputBitmap->columns()/2,
                                                              yLimit0*2.5,
                                                              1,1,
                                                              IMGNOFLIP
                                                              );   
                         }    
		                
	}
	DT("m0_2");
		                //GImageEditor *editor=GImageEditor::create();
		                //editor->WriteImageData(lineScale,"/__XML__/1.jpg",0); //exit(0);
	DT("m1");

						setMatrix->letterCorrelation(matchLine,
													 aliKali,
                                                     indexLine,
													 LimY0*scale, 
													 (LimY0+textLineSize)*scale,
													 0,   //no scale mode
                                                     OCRMode,
													 0);  //0 no print

		DT2("m3");
						#ifndef REPORT_DRAW
		                setMatrix->destroy();
                        #endif
        


cout<<"new string"<<endl; sleep(10);

						//now we start check result of correlation and make decision about place of every letter
	   DT2("m3_1");					//разборка строки на буквы в соответствии с величиной корреляции и вложенностью букв. например О-С
        
	}
    TIME_PRINT
	    DT2("start grammar corrector"<<END);	
       // logicProcessor->grammarCorrector(strArray,correctionTable,mainString,xmlString,LOCAL_MODE);
        
        DT2("done Grammar"<<endl);
   
    
	
 }////////////////////////////////////////////////////////////////////////////////
Ejemplo n.º 9
0
void TerrainFile::import(  const GBitmap &heightMap, 
                           F32 heightScale,
                           const Vector<U8> &layerMap, 
                           const Vector<String> &materials,
                           bool flipYAxis )
{
   AssertFatal( heightMap.getWidth() == heightMap.getHeight(), "TerrainFile::import - Height map is not square!" );
   AssertFatal( isPow2( heightMap.getWidth() ), "TerrainFile::import - Height map is not power of two!" );

   const U32 newSize = heightMap.getWidth();
   if ( newSize != mSize )
   {
      mHeightMap.setSize( newSize * newSize );
      mHeightMap.compact();
      mSize = newSize;
   }

   // Convert the height map to heights.
   U16 *oBits = mHeightMap.address();
   if ( heightMap.getFormat() == GFXFormatR5G6B5 )
   {
      const F32 toFixedPoint = ( 1.0f / (F32)U16_MAX ) * floatToFixed( heightScale );
      const U16 *iBits = (const U16*)heightMap.getBits();
      if ( flipYAxis )
      {
         for ( U32 i = 0; i < mSize * mSize; i++ )
         {
            U16 height = convertBEndianToHost( *iBits );
            *oBits = (U16)mCeil( (F32)height * toFixedPoint );
            ++oBits;
            ++iBits;
         }
      }
      else
      {
         for(S32 y = mSize - 1; y >= 0; y--) {
            for(U32 x = 0; x < mSize; x++) {
               U16 height = convertBEndianToHost( *iBits );
               mHeightMap[x + y * mSize] = (U16)mCeil( (F32)height * toFixedPoint );
               ++iBits;
            }
         }
      }
   }
   else
   {
      const F32 toFixedPoint = ( 1.0f / (F32)U8_MAX ) * floatToFixed( heightScale );
      const U8 *iBits = heightMap.getBits();
      if ( flipYAxis )
      {
         for ( U32 i = 0; i < mSize * mSize; i++ )
         {
            *oBits = (U16)mCeil( ((F32)*iBits) * toFixedPoint );
            ++oBits;
            iBits += heightMap.getBytesPerPixel();
         }
      }
      else
      {
         for(S32 y = mSize - 1; y >= 0; y--) {
            for(U32 x = 0; x < mSize; x++) {
               mHeightMap[x + y * mSize] = (U16)mCeil( ((F32)*iBits) * toFixedPoint );
               iBits += heightMap.getBytesPerPixel();
            }
         }
      }
   }

   // Copy over the layer map.
   AssertFatal( layerMap.size() == mHeightMap.size(), "TerrainFile::import - Layer map is the wrong size!" );
   mLayerMap = layerMap;
   mLayerMap.compact();

   // Resolve the materials.
   _resolveMaterials( materials );

   // Rebuild the collision grid map.
   _buildGridMap();
}
void blTerrainProxy::light(LightInfo * light)
{
   // If we don't have terrain or its not a directional
   // light then skip processing.
   TerrainBlock * terrain = getObject();
   if ( !terrain || light->getType() != LightInfo::Vector )
      return;

   S32 time = Platform::getRealMilliseconds();

   // reset
   mShadowVolume = new ShadowVolumeBSP;

   // build interior shadow volume
   for(ObjectProxy ** itr = gLighting->mLitObjects.begin(); itr != gLighting->mLitObjects.end(); itr++)
   {
      ObjectProxy* objproxy = *itr;
      if (markObjectShadow(objproxy))
         objproxy->addToShadowVolume(mShadowVolume, light, SceneLighting::SHADOW_DETAIL);
   }

   lightVector(light);

   // set the lightmap...
   terrain->clearLightMap();

   // Blur...
   F32 kernel[3][3] = { {1, 2, 1},
                        {2, 3, 2},
                        {1, 2, 1} };

   F32 modifier = 1;
   F32 divisor = 0;


   for( U32 i=0; i<3; i++ )
   {
      for( U32 j=0; j<3; j++ )
      {
         if( i==1 && j==1 )
         {
            kernel[i][j] = 1 + kernel[i][j] * modifier;
         }
         else
         {
            kernel[i][j] = kernel[i][j] * modifier;
         }

         divisor += kernel[i][j];
      }
   }

   for( U32 i=0; i < mLightMapSize; i++ )
   {
      for( U32 j=0; j < mLightMapSize; j++ )
      {

         ColorF val;
         val  = _getValue( i-1, j-1  ) * kernel[0][0];
         val += _getValue( i-1, j    ) * kernel[0][1];
         val += _getValue( i-1, j+1  ) * kernel[0][2];
         val += _getValue(   i, j-1  ) * kernel[1][0];
         val += _getValue(   i, j    ) * kernel[1][1];
         val += _getValue(   i, j+1  ) * kernel[1][2];
         val += _getValue( i+1, j-1  ) * kernel[2][0];
         val += _getValue( i+1, j    ) * kernel[2][1];
         val += _getValue( i+1, j+1  ) * kernel[2][2];

         U32 edge = 0;

         if( j == 0 || j == mLightMapSize - 1 )
            edge++;

         if( i == 0 || i == mLightMapSize - 1 )
            edge++;

         if( !edge )
            val = val / divisor;
         else
            val = mLightmap[ i * mLightMapSize + j ];

         // clamp values
         mLightmap[ i * mLightMapSize + j ]= val;
      }
   }

   // And stuff it into the texture...
   GBitmap *terrLightMap = terrain->getLightMap();
   for(U32 y = 0; y < mLightMapSize; y++)
   {
      for(U32 x = 0; x < mLightMapSize; x++)
      {
         ColorI color(255, 255, 255, 255);
         
         color.red   = mLightmap[x + y * mLightMapSize].red   * 255;
         color.green = mLightmap[x + y * mLightMapSize].green * 255;
         color.blue  = mLightmap[x + y * mLightMapSize].blue  * 255;

         terrLightMap->setColor(x, y, color);
      }
   }

   /*
   // This handles matching up the outer edges of the terrain
   // lightmap when it has neighbors
   if (!terrain->isTiling())
   {
      for (S32 y = 0; y < terrLightMap->getHeight(); y++)
      {
         ColorI c;
         if (terrain->getFile()->mEdgeTerrainFiles[0])
         {
            terrLightMap->getColor(terrLightMap->getWidth()-1,y,c);
            terrLightMap->setColor(0,y,c);
            terrLightMap->setColor(1,y,c);
         }
         else
         {
            terrLightMap->getColor(0,y,c);
            terrLightMap->setColor(terrLightMap->getWidth()-1,y,c);
            terrLightMap->setColor(terrLightMap->getWidth()-2,y,c);
         }
      }

      for (S32 x = 0; x < terrLightMap->getHeight(); x++)
      {
         ColorI c;
         if (terrain->getFile()->mEdgeTerrainFiles[1])
         {
            terrLightMap->getColor(x,terrLightMap->getHeight()-1,c);
            terrLightMap->setColor(x,0,c);
            terrLightMap->setColor(x,1,c);
         }
         else
         {
            terrLightMap->getColor(x,0,c);
            terrLightMap->setColor(x,terrLightMap->getHeight()-1,c);
            terrLightMap->setColor(x,terrLightMap->getHeight()-2,c);
         }
      }
   }
   */

   delete mShadowVolume;

   Con::printf("    = terrain lit in %3.3f seconds", (Platform::getRealMilliseconds()-time)/1000.f);
}
Ejemplo n.º 11
0
void GMainEditor::startOCRBatch(){
    
    imageEditor=(GImageEditor*)inputData.imageEditor;
    fontEditor=(GFontEditor*)inputData.fontEditor;
    logicProcessor=(GLogicProcessor*)inputData.logicProcessor;
    if(!logicProcessor->dictionaryReady)logicProcessor->readDictionary();
    aliKali=fontEditor->aliKali;

    
	string strHeaderHTML,srcLine,str;
	string  path=inputData.data["tablePath"]+"/header.xml";
	DIR *dir;
	//int mode;	
	int i=0;
    //читаем статистику использования букв книги
    //readLetterStat();
    //загружаем базу данных букв
    
#ifdef FORK
     int maxFork=inputData.num_cores*0.75;
     int pid;
     pidID *pidIDArray;
     
    
    if(inputData.fileList.size()>1){
        int countFork=0; 
        MemoryFile *pidData_mf;  //main file for conection with child process
        //inputData.data["statPath"].c_str()
        time_t seconds;    seconds = time (NULL);
        ostringstream out;  out<<"/tmp/"<<seconds;
        string path=out.str();
        pidData_mf=MemoryFile::create(path.c_str(), MemoryFile::if_exists_keep_if_dont_exists_create);
        pidData_mf->resize(sizeof(pidID)*maxFork);
        pidIDArray=(pidID*)pidData_mf->data(); //array which can be share between processes.
        
        
        for(int index=0;index<maxFork;index++){
            //cout_<<"pidIDArray["<<index<<"].status="<<pidIDArray[index].status<<endl;
            pidIDArray[index].status=0;
        }

        int ID=0;
        
        while(i<inputData.fileList.size()){
            cout<<"NEW file#1 "<<inputData.fileList[i]<<endl;
            if( ( dir=opendir(inputData.fileList[i].c_str()))!=NULL){
                i++; continue;
            }	
            
            inputData.data["inputFile"]=inputData.fileList[i];
            
            string  path=inputData.data["inputFile"];    //проверяем есть ли такой распознаный файл
            path=substr(0,(int)path.rfind("."),path);
            string volume=path;
            string fileIndex=fileName(path);
            path+=".xml";
            if(is_file(path)){i++; continue;}
            
            
            if(!forkProccesOCR_(pidIDArray,ID,maxFork)){
                cout<<"ERROR on fork return";
                sleep(1); continue; 
            };
            i++;if(i==inputData.fileList.size())break;
            countFork++;

            for(int index=0;index<maxFork;index++)cout<<pidIDArray[index].status<<" ";
            cout<<endl;
            
            ID=100; int status;
            while(ID==100){
                if(countFork<=maxFork){     //есть свободные слоты для новых процессов
                    for(int index=0;index<maxFork;index++){  //маркируем слот как занятый
                        //cout_<<"pidIDArray["<<index<<"].status="<<pidIDArray[index].status<<endl;
                        if(pidIDArray[index].status==0){
                          ID=index;
                          pidIDArray[index].status=1;
                          break;
                        }    
                    }
                }else{
                    int forkStatusCount=0;
                    for(int index=0;index<maxFork;index++){   //подсчитываем количество активных процессов
                        if(pidIDArray[index].status==1)forkStatusCount++;
                    }
                    if(forkStatusCount==countFork){sleep(1); continue;};     //ждем завершения процесса
                    wait(&status);                           //регистрируем с системе завершенный процесс
                    countFork--;
                }   
            }	
        }
            
    }else{
        inputData.data["inputFile"]=inputData.fileList[0];
        pechaImg=LoadImageData(inputData.data["inputFile"],0); 
        startOCR(pechaImg);
    }
    
    
#else
    cout<<"NO FORK";
	while(i<inputData.fileList.size()){
		if( ( dir=opendir(inputData.fileList[i].c_str()))!=NULL){
			readDirectoryToArray(inputData.fileList, inputData.fileList[i],"img");
			i++; continue;
		}	
		GBitmap* pechaImg;
		inputData.data["inputFile"]=inputData.fileList[i];

        if(!is_file(inputData.data["inputFile"]))continue;
        pechaImg=LoadImageData(inputData.data["inputFile"],0); 
        if(!pechaImg){cout_<<"no open file"<<inputData.data["inputFile"]<<endl; return;}
        
        str=inputData.data["inputFile"];
		str=substr(0,str.rfind("."),str);
		str+=".html";   //cout_<<str<<endl;
		inputData.data["inputFileName"]=inputData.data["siteName"];
		inputData.data["inputFileName"]+=substr(inputData.data["siteRoot"].size(),inputData.data["inputFile"]);

		//readPageHTML();
		inputData.c_out.open(str.c_str());
		pechaDataLoaded=0;
		startOCR(pechaImg);
        pechaImg->destroy();
		inputData.c_out.close();
		i++;
	}
	
#endif		
	//drawLettersInCorrectionTable(DRAW_BASE);
	
	cout_<<"COMPLETE"<<endl;
	
}//____________________________________________________________________________
Ejemplo n.º 12
0
	/**
	 *	Scale and translate the bitmap such that is fills the specific rectangle.
	 *
	 *	Any area in the rectangle that is outside of the bounds of the canvas is ignored.
	 *
	 *	If a given pixel in the bitmap is not opaque (e.g. GPixel_GetA() < 255) then blend it
	 *	using SRCOVER blend mode.
	 */
	void fillBitmapRect(const GBitmap& src, const GRect& dst) {
		// Store dimensions of dst rectangle
		int left = dst.left();
		int top = dst.top();
		int right = dst.right();
		int bottom = dst.bottom();
		int rW = dst.width();
		int rH = dst.height();

		// Store dimensions of src bitmap
		int bW = src.width();
		int bH = src.height();

		// Find necessary scale factor for both width and height
		float sx = (float)bW/(float)rW;
		float sy = (float)bH/(float)rH;

		float tx = 0 - left;
		float ty = 0 - top;

		// Create matrix for scaling
		float scale[6] = 
			{sx, 0, 0, 
			 0, sy, 0};

		// Create matrix for translating
		float translate[6] =
			{1, 0, tx,
			 0, 1, ty};

		// Combine the matrices
		float both[6] = 
			{sx, 0, sx*tx,
			 0, sy, sy*ty};

		// For loop that takes each point in dst and find corresponding point in src
		GPixel* d = draw.fPixels;
		d = (GPixel*)((char*)d + (int)top * draw.rowBytes());
		for (int y = top; y < bottom; ++y) {
			for (int x = left; x < right; ++x) {
				// Only map pixels that are in the bitmap
				if ((x >= 0 && x < draw.width()) && (y >= 0 && y < draw.height())) {
					// Find corresponding point in src bitmap
					int xP;
					int yP;

					xP = both[0] * x + both[1] * y + both[2];
					yP = both[3] * x + both[4] * y + both[5];

					// Apply CTM to x and y
					int nX = (CTM[0] * x) + (CTM[1] * y) + (CTM[2]);
					int nY = (CTM[3] * x) + (CTM[4] * y) + (CTM[5]);

					// Prepare color to fill
					unsigned a = GPixel_GetA(*src.getAddr(xP, yP));
					unsigned r = GPixel_GetR(*src.getAddr(xP, yP));
					unsigned g = GPixel_GetG(*src.getAddr(xP, yP));
					unsigned b = GPixel_GetB(*src.getAddr(xP, yP));

					unsigned nA = a;
					unsigned nR = r;
					unsigned nG = g;
					unsigned nB = b;

					if (GPixel_GetA(*draw.getAddr(x, y)) > 0) {
	    	    		// blend
						float lA = GPixel_GetA(*src.getAddr(xP, yP)) / 255.99999;
						float lR = GPixel_GetR(*src.getAddr(xP, yP)) / 255.99999;
						float lG = GPixel_GetG(*src.getAddr(xP, yP)) / 255.99999;
						float lB = GPixel_GetB(*src.getAddr(xP, yP)) / 255.99999;

						
						float sA = lA + ((GPixel_GetA(*draw.getAddr(x, y)) / 255.99999) * (1.0f - lA));
						float sR = lR + ((GPixel_GetR(*draw.getAddr(x, y)) / 255.99999) * (1.0f - lA));
						float sG = lG + ((GPixel_GetG(*draw.getAddr(x, y)) / 255.99999) * (1.0f - lA));
						float sB = lB + ((GPixel_GetB(*draw.getAddr(x, y)) / 255.99999) * (1.0f - lA));
						

						/*
						float sA = lA + ((GPixel_GetA(*draw.getAddr(nX, nY)) / 255.99999) * (1.0f - lA));
						float sR = lR + ((GPixel_GetR(*draw.getAddr(nX, nY)) / 255.99999) * (1.0f - lA));
						float sG = lG + ((GPixel_GetG(*draw.getAddr(nX, nY)) / 255.99999) * (1.0f - lA));
						float sB = lB + ((GPixel_GetB(*draw.getAddr(nX, nY)) / 255.99999) * (1.0f - lA));
						*/
										
						nA = (int)(sA * 255.99999);
						nR = (int)(sR * 255.99999);
						nG = (int)(sG * 255.99999);
						nB = (int)(sB * 255.99999);

	    	    		d[x] = GPixel_PackARGB(nA, nR, nG, nB);
	    	    		//d = (GPixel*)((char*)d + (int)nY * draw.rowBytes());
	    	    		//d[nX] = GPixel_PackARGB(nA, nR, nG, nB);
	    	    		//d = (GPixel*)((char*)d - (int)nY * draw.rowBytes());
    	    		} else {
    	    			d[x] = GPixel_PackARGB(a, r, g, b);
    	    			//d = (GPixel*)((char*)d + (int)nY * draw.rowBytes());
	    	    		//d[nX] = GPixel_PackARGB(a, r, g, b);
	    	    		//d = (GPixel*)((char*)d - (int)nY * draw.rowBytes());
    	    		}	    		
				}
			}
			d = (GPixel*)((char*)d + draw.rowBytes());
		}
	}
Ejemplo n.º 13
0
 BitmapShape(const GBitmap& bm) : fBM(bm) {
     const int w = std::max(bm.width(), 100);
     const int h = std::max(bm.height(), 100);
     fRect = GRect::MakeXYWH(100, 100, w, h);
 }
Ejemplo n.º 14
0
void ScreenShot::capture( GuiCanvas *canvas )
{
   AssertFatal( mPending, "ScreenShot::capture() - The capture wasn't pending!" );

   if ( mTiles == 1 )
   {
      _singleCapture( canvas );
      return;
   }

   char filename[256];

   Point2I canvasSize = canvas->getPlatformWindow()->getVideoMode().resolution;
   
   // Calculate the real final size taking overlap in account
   Point2I overlapPixels( canvasSize.x * mPixelOverlap.x, canvasSize.y * mPixelOverlap.y );   

   // Calculate the overlap to be used by the frustum tiling, 
   // so it properly expands the frustum to overlap the amount of pixels we want
   mFrustumOverlap.x = ((F32)canvasSize.x/(canvasSize.x - overlapPixels.x*2)) * ((F32)overlapPixels.x/(F32)canvasSize.x);
   mFrustumOverlap.y = ((F32)canvasSize.y/(canvasSize.y - overlapPixels.y*2)) * ((F32)overlapPixels.y/(F32)canvasSize.y);
   
   //overlapPixels.set(0,0);
   // Get a buffer to write a row of tiles into.
   GBitmap *outBuffer = new GBitmap( canvasSize.x * mTiles - overlapPixels.x * mTiles * 2 , canvasSize.y - overlapPixels.y );

   // Open up the file on disk.
   dSprintf( filename, 256, "%s.%s", mFilename, "png" );
   FileStream fs;
   if ( !fs.open( filename, Torque::FS::File::Write ) )
      Con::errorf( "ScreenShot::capture() - Failed to open output file '%s'!", filename );

   // Open a PNG stream for the final image
   DeferredPNGWriter pngWriter;
   pngWriter.begin(outBuffer->getFormat(), outBuffer->getWidth(), canvasSize.y * mTiles - overlapPixels.y * mTiles * 2, fs, 0);
   
   // Render each tile to generate a huge screenshot.
   for( U32 ty=0; ty < mTiles; ty++ )
   {
      for( S32 tx=0; tx < mTiles; tx++ )
      {
         // Set the current tile offset for tileFrustum().
         mCurrTile.set( tx, mTiles - ty - 1 );

         // Let the canvas render the scene.
         canvas->renderFrame( false );

         // Now grab the current back buffer.
         GBitmap *gb = _captureBackBuffer();

			// The current GFX device couldn't capture the backbuffer or it's unable of doing so.
			if (gb == NULL)
				return;

                  
         // Copy the captured bitmap into its tile
         // within the output bitmap.         
         const U32 inStride = gb->getWidth() * gb->getBytesPerPixel();         
         const U8 *inColor = gb->getBits() + inStride * overlapPixels.y;
         const U32 outStride = outBuffer->getWidth() * outBuffer->getBytesPerPixel();
         const U32 inOverlapOffset = overlapPixels.x * gb->getBytesPerPixel();         
         const U32 inOverlapStride = overlapPixels.x * gb->getBytesPerPixel()*2;
         const U32 outOffset = (tx * (gb->getWidth() - overlapPixels.x*2 )) * gb->getBytesPerPixel();
         U8 *outColor = outBuffer->getWritableBits() + outOffset;
         for( U32 row=0; row < gb->getHeight() - overlapPixels.y; row++ )
         {
            dMemcpy( outColor, inColor + inOverlapOffset, inStride - inOverlapStride );
            
            //Grandient blend the left overlap area of this tile over the previous tile left border
            if (tx && !(ty && row < overlapPixels.y))
            {
               U8 *blendOverlapSrc = (U8*)inColor;
               U8 *blendOverlapDst = outColor - inOverlapOffset;
               for ( U32 px=0; px < overlapPixels.x; px++)
               {
                  F32 blendFactor = (F32)px / (F32)overlapPixels.x;
                  sBlendPixelRGB888(blendOverlapSrc, blendOverlapDst, blendFactor);                 

                  blendOverlapSrc += gb->getBytesPerPixel();
                  blendOverlapDst += outBuffer->getBytesPerPixel();                   
               }               
            }

            //Gradient blend against the rows the excess overlap rows already in the buffer            
            if (ty && row < overlapPixels.y)
            {
               F32 rowBlendFactor = (F32)row / (F32)overlapPixels.y;
               U8 *blendSrc = outColor + outStride * (outBuffer->getHeight() - overlapPixels.y);
               U8 *blendDst = outColor;               
               for ( U32 px=0; px < gb->getWidth() - overlapPixels.x*2; px++)
               {                  
                  sBlendPixelRGB888(blendSrc, blendDst, 1.0-rowBlendFactor); 
                  blendSrc += gb->getBytesPerPixel();
                  blendDst += outBuffer->getBytesPerPixel();                   
               }                              
            }

            
            inColor += inStride;
            outColor += outStride;
         }

         delete gb;
      }

      // Write the captured tile row into the PNG stream
      pngWriter.append(outBuffer, outBuffer->getHeight()-overlapPixels.y);
   }

   //Close the PNG stream
   pngWriter.end();
   
   // We captured... clear the flag.
   mPending = false;
}
Ejemplo n.º 15
0
void ImposterCapture::_separateAlpha( GBitmap *imposterOut )
{
   PROFILE_START(TSShapeInstance_snapshot_sb_separate);

   // TODO: Remove all this when we get rid of the 'render on black/white'.

      // now separate the color and alpha channels
      GBitmap *bmp = new GBitmap;
      bmp->allocateBitmap(mDim, mDim, false, GFXFormatR8G8B8A8);
      U8 * wbmp = (U8*)mWhiteBmp->getBits(0);
      U8 * bbmp = (U8*)mBlackBmp->getBits(0);
      U8 * dst  = (U8*)bmp->getBits(0);

      const U32 pixCount = mDim * mDim;

      // simpler, probably faster...
      for ( U32 i=0; i < pixCount; i++ )
      {
         // Shape on black background is alpha * color, shape on white 
         // background is alpha * color + (1-alpha) * 255 we want 255 *
         // alpha, or 255 - (white - black).
         //
         // JMQ: or more verbosely:
         //  cB = alpha * color + (0 * (1 - alpha))
         //  cB = alpha * color
         //  cW = alpha * color + (255 * (1 - alpha))
         //  cW = cB + (255 * (1 - alpha))
         // solving for alpha
         //  cW - cB = 255 * (1 - alpha)
         //  (cW - cB)/255 = (1 - alpha)
         //  alpha = 1 - (cW - cB)/255
         // since we want alpha*255, multiply through by 255
         //  alpha * 255 = 255 - cW - cB
         U32 alpha = 255 - (wbmp[i*3+0] - bbmp[i*3+0]);
         alpha    += 255 - (wbmp[i*3+1] - bbmp[i*3+1]);
         alpha    += 255 - (wbmp[i*3+2] - bbmp[i*3+2]);

         if ( alpha != 0 )
         {
            F32 floatAlpha = ((F32)alpha)/(3.0f*255.0f); 
            dst[i*4+0] = (U8)(bbmp[i*3+0] / floatAlpha);
            dst[i*4+1] = (U8)(bbmp[i*3+1] / floatAlpha);
            dst[i*4+2] = (U8)(bbmp[i*3+2] / floatAlpha);

            // Before we assign the alpha we "fizzle" the value
            // if its greater than 84.  This causes the imposter
            // to dissolve instead of popping into view.
            alpha /= 3;
            dst[i*4+3] = (U8)alpha;
         }
         else
         {
            dst[i*4+0] = dst[i*4+1] = dst[i*4+2] = dst[i*4+3] = 0;
         }
      }

   PROFILE_END(); // TSShapeInstance_snapshot_sb_separate
  
   PROFILE_START(TSShapeInstance_snapshot_sb_filter);

      // We now run a special kernel filter over the image that
      // averages color into the transparent areas.  This should
      // in essence give us a border around the edges of the 
      // imposter silhouette which fixes the artifacts around the
      // alpha test billboards.
      U8* dst2 = (U8*)imposterOut->getBits(0);

      _colorAverageFilter( mDim, dst, dst2 );
      
      if ( 0 )
      {
         FileStream fs;
         if ( fs.open( "./imposterout.png", Torque::FS::File::Write ) )
            imposterOut->writeBitmap( "png", fs );

         fs.close();

         if ( fs.open( "./temp.png", Torque::FS::File::Write ) )
            bmp->writeBitmap( "png", fs );

         fs.close();
      }
   

   PROFILE_END(); // TSShapeInstance_snapshot_sb_filter

   delete bmp;
}
Ejemplo n.º 16
0
static void
maskedSubsample(const GPixmap* img,
                const GBitmap *p_mask,
                GPixmap& subsampled_image,
                GBitmap& subsampled_mask,
                int gridwidth, int inverted_mask, 
                int minpixels=1
                )
{
  const GPixmap& image= *img;
  const GBitmap& mask = *p_mask;
  int imageheight = image.rows();
  int imagewidth = image.columns();
  // compute the size of the resulting subsampled image
  int subheight = imageheight/gridwidth;
  if(imageheight%gridwidth)
    subheight++;
  int subwidth = imagewidth/gridwidth;
  if(imagewidth%gridwidth)
    subwidth++;
  // set the sizes unless in incremental mode
  subsampled_image.init(subheight, subwidth);
  subsampled_mask.init(subheight, subwidth);
  // go subsampling
  int row, col;  // row and col in the subsampled image
  int posx, posxend, posy, posyend; // corresponding square in the original image
  for(row=0, posy=0; row<subheight; row++, posy+=gridwidth)
    {
      GPixel* subsampled_image_row = subsampled_image[row]; // row row of subsampled image
      unsigned char* subsampled_mask_row = subsampled_mask[row]; // row row of subsampled mask
      posyend = posy+gridwidth;
      if(posyend>imageheight)
        posyend = imageheight;
      for(col=0, posx=0; col<subwidth; col++, posx+=gridwidth) 
        {
          posxend = posx+gridwidth;
          if(posxend>imagewidth)
            posxend = imagewidth;
          int count = 0;
          int r = 0;
          int g = 0;
          int b = 0;
          for(int y=posy; y<posyend; y++)
            {
              const unsigned char* mask_y = mask[y]; // Row y of the mask
              for(int x=posx; x<posxend; x++)
                {
                  unsigned char masked = (inverted_mask ? !mask_y[x] :mask_y[x]);
                  if(!masked)
                    {
                      GPixel p = image[y][x];
                      r += p.r;
                      g += p.g;
                      b += p.b;
                      count ++;
                    }
                }
            }
          /* minpixels pixels are enough to give the color */
          /* so set it, and do not mask this point */
          if(count >= minpixels)   
            {
              GPixel p;
              p.r = r/count;
              p.g = g/count;
              p.b = b/count;
              subsampled_image_row[col] = p;
              subsampled_mask_row[col] = 0;
            } 
          else /* make it bright red and masked */ 
            {
              subsampled_image_row[col] = GPixel::RED;
              subsampled_mask_row[col] = 1;
            }
        }
    }
}
Ejemplo n.º 17
0
  // This code draws a bitmap assuming that we only have translation and scale,
  // which allows us to perform certain optimizations...
  void drawBitmapSimple(const GBitmap &bm, const GPaint &paint) {
    const GBitmap &ctxbm = GetInternalBitmap();
    GRect ctxRect = GRect::MakeXYWH(0, 0, ctxbm.width(), ctxbm.height());
    GRect bmRect = GRect::MakeXYWH(0, 0, bm.width(), bm.height());
    GRect pixelRect = GetTransformedBoundingBox(bmRect);

    GRect rect;
    if(!(rect.setIntersection(ctxRect, pixelRect))) {
      return;
    }

    // We know that since we're only doing scale and translation, that all of the pixel
    // centers contained in rect are going to be drawn, so we only need to know the
    // dimensions of the mapping...
    GVec3f origin(0, 0, 1);
    GVec3f offset(1, 1, 1);

    origin = m_CTM * origin;
    offset = m_CTM * offset;

    float xScale = 1.0f / (offset.X() - origin.X());
    float yScale = 1.0f / (offset.Y() - origin.Y());

    GVec2f start = GVec2f(0, 0);
    if(xScale < 0.0f) {
      start.X() = pixelRect.fRight - 1.0f;
    }
    if(yScale < 0.0f) {
      start.Y() = pixelRect.fBottom - 1.0f;
    }

    GIRect dstRect = rect.round();

    // Construct new bitmap
    int32_t offsetX = ::std::max(0, -dstRect.fLeft);
    int32_t offsetY = ::std::max(0, -dstRect.fTop);
    GBitmap fbm;
    fbm.fWidth = bm.width();
    fbm.fHeight = bm.height();
    fbm.fPixels = GetRow(bm, offsetY) + offsetX;
    fbm.fRowBytes = bm.fRowBytes;

    BlendFunc blend = blend_srcover;

    float alpha = paint.getAlpha();
    if(alpha >= kOpaqueAlpha) {
      for(uint32_t j = 0; j < dstRect.height(); j++) {

        uint32_t srcIdxY = static_cast<uint32_t>(start.Y() + static_cast<float>(j) * yScale);
        GPixel *srcPixels = GetRow(fbm, Clamp<int>(srcIdxY, 0, fbm.height()));
        GPixel *dstPixels = GetRow(ctxbm, dstRect.fTop + j) + dstRect.fLeft;

        for(uint32_t i = 0; i < dstRect.width(); i++) {
          uint32_t srcIdxX = static_cast<uint32_t>(start.X() + static_cast<float>(i) * xScale);
          dstPixels[i] = blend(dstPixels[i], srcPixels[Clamp<int>(srcIdxX, 0, fbm.width())]);
        }
      }
    } else {
      const uint32_t alphaVal = static_cast<uint32_t>((alpha * 255.0f) + 0.5f);
      for(uint32_t j = 0; j < dstRect.height(); j++) {

        uint32_t srcIdxY = static_cast<uint32_t>(start.Y() + static_cast<float>(j) * yScale);
        GPixel *srcPixels = GetRow(fbm, srcIdxY);
        GPixel *dstPixels = GetRow(ctxbm, dstRect.fTop + j) + dstRect.fLeft;

        for(uint32_t i = 0; i < dstRect.width(); i++) {
          uint32_t srcIdxX = static_cast<uint32_t>(start.X() + static_cast<float>(i) * xScale);
          uint32_t srcA = fixed_multiply(GPixel_GetA(srcPixels[srcIdxX]), alphaVal);
          uint32_t srcR = fixed_multiply(GPixel_GetR(srcPixels[srcIdxX]), alphaVal);
          uint32_t srcG = fixed_multiply(GPixel_GetG(srcPixels[srcIdxX]), alphaVal);
          uint32_t srcB = fixed_multiply(GPixel_GetB(srcPixels[srcIdxX]), alphaVal);
          GPixel src = GPixel_PackARGB(srcA, srcR, srcG, srcB);
          dstPixels[i] = blend(dstPixels[i], src);
        }
      }
    }
  }