Ejemplo n.º 1
0
DrawTarget*
ContentClientIncremental::BorrowDrawTargetForPainting(const PaintState& aPaintState,
        RotatedContentBuffer::DrawIterator* aIter)
{
    if (aPaintState.mMode == SurfaceMode::SURFACE_NONE) {
        return nullptr;
    }

    if (aIter) {
        if (aIter->mCount++ > 0) {
            return nullptr;
        }
        aIter->mDrawRegion = aPaintState.mRegionToDraw;
    }

    DrawTarget* result = nullptr;

    nsIntRect drawBounds = aPaintState.mRegionToDraw.GetBounds();
    MOZ_ASSERT(!mLoanedDrawTarget);

    // BeginUpdate is allowed to modify the given region,
    // if it wants more to be repainted than we request.
    if (aPaintState.mMode == SurfaceMode::SURFACE_COMPONENT_ALPHA) {
        nsIntRegion drawRegionCopy = aPaintState.mRegionToDraw;
        RefPtr<DrawTarget> onBlack = GetUpdateSurface(BUFFER_BLACK, drawRegionCopy);
        RefPtr<DrawTarget> onWhite = GetUpdateSurface(BUFFER_WHITE, aPaintState.mRegionToDraw);
        if (onBlack && onWhite) {
            NS_ASSERTION(aPaintState.mRegionToDraw == drawRegionCopy,
                         "BeginUpdate should always modify the draw region in the same way!");
            FillSurface(onBlack, aPaintState.mRegionToDraw, nsIntPoint(drawBounds.x, drawBounds.y), gfxRGBA(0.0, 0.0, 0.0, 1.0));
            FillSurface(onWhite, aPaintState.mRegionToDraw, nsIntPoint(drawBounds.x, drawBounds.y), gfxRGBA(1.0, 1.0, 1.0, 1.0));
            mLoanedDrawTarget = Factory::CreateDualDrawTarget(onBlack, onWhite);
        } else {
            mLoanedDrawTarget = nullptr;
        }
    } else {
        mLoanedDrawTarget = GetUpdateSurface(BUFFER_BLACK, aPaintState.mRegionToDraw);
    }
    if (!mLoanedDrawTarget) {
        NS_WARNING("unable to get context for update");
        return nullptr;
    }

    result = mLoanedDrawTarget;
    mLoanedTransform = mLoanedDrawTarget->GetTransform();
    mLoanedTransform.Translate(-drawBounds.x, -drawBounds.y);
    result->SetTransform(mLoanedTransform);
    mLoanedTransform.Translate(drawBounds.x, drawBounds.y);

    if (mContentType == gfxContentType::COLOR_ALPHA) {
        gfxUtils::ClipToRegion(result, aPaintState.mRegionToDraw);
        nsIntRect bounds = aPaintState.mRegionToDraw.GetBounds();
        result->ClearRect(Rect(bounds.x, bounds.y, bounds.width, bounds.height));
    }

    return result;
}
Ejemplo n.º 2
0
	CBenchmark()
	{
		for (dword i = 0; i < 4; i++)
			m_PerfData[i] = 0;

		m_SurfID = CreateSurface(300, 200, 256, 256 + 16);
		FillSurface(m_SurfID, 0xFFFFFFFF);
		DrawFrameRect(m_SurfID, 0, 0, 256, 256 + 16, 0xFF808080);
		ShowSurface(m_SurfID);

		KeEnableNotification(Nf_VirtualKey);
		KeEnableNotification(NfKe_TerminateProcess);

		double T = 0.0;
		dword FPS = 0;
		dword T1 = KeGetTime();
		for (;;)
		{
			for (dword i = 0; i < 6; i++)
				DrawIter(T - 4.0 * (5 - i), 0xFFFFFFFF);

			T += 1.0;
			for (dword i = 0; i < 6; i++)
				DrawIter(T - 4.0 * (5 - i), 0x004070FF | (((i + 1) * 0x10) << 24));

			WaitRedraw();
			FPS++;

			dword T2 = KeGetTime();
			if (T2 - T1 >= 1000)
			{
				dword AvgFPS = AddPerfData(FPS);
				T1 = T2;
				FPS = 0;

				ShowStat(AvgFPS);
			}


			CNotification<4> Nf;
			dword NfCount = KeGetNotificationCount();
			for (dword i = 0; i < NfCount; i++)
			{
				Nf.Recv();
				if (Nf.GetID() == Nf_VirtualKey)
				{
					if (Nf.GetByte(0) == VK_Esc)
						return;
				}
				else if (Nf.GetID() == NfKe_TerminateProcess)
					return;
			}
		}
	}
Ejemplo n.º 3
0
///////////////////
// Precalculate a font's colour
void CFont::PreCalculate(const SmartPointer<SDL_Surface> & bmpSurf, Color colour) {
	Uint32 pixel;
	int x, y;

	FillSurface(bmpSurf.get(), SDL_MapRGBA(bmpSurf.get()->format, 255, 0, 255, 0));

	// Lock the surfaces
	LOCK_OR_QUIT(bmpSurf);
	LOCK_OR_QUIT(bmpFont);

	Uint8 R, G, B, A;
	const Uint8 sr = colour.r, sg = colour.g, sb = colour.b;

	// Outline font: replace white pixels with appropriate color, put black pixels
	if (OutlineFont) {
		for (y = 0; y < bmpSurf.get()->h; y++) {
			for (x = 0; x < bmpSurf.get()->w; x++) {
				pixel = GetPixel(bmpFont.get(), x, y);
				GetColour4(pixel, bmpFont.get()->format, &R, &G, &B, &A);

				if (R == 255 && G == 255 && B == 255)    // White
					PutPixel(bmpSurf.get(), x, y,
					         SDL_MapRGBA(bmpSurf.get()->format, sr, sg, sb, A));
				else if (!R && !G && !B)   // Black
					PutPixel(bmpSurf.get(), x, y,
					         SDL_MapRGBA(bmpSurf.get()->format, 0, 0, 0, A));
			}
		}
	// Not outline: replace black pixels with appropriate color
	} else {
		for (y = 0; y < bmpSurf.get()->h; y++) {
			for (x = 0; x < bmpSurf.get()->w; x++) {
				pixel = GetPixel(bmpFont.get(), x, y);
				GetColour4(pixel, bmpFont.get()->format, &R, &G, &B, &A);

				if (!R && !G && !B)   // Black
					PutPixel(bmpSurf.get(), x, y,
					         SDL_MapRGBA(bmpSurf.get()->format, sr, sg, sb, A));
			}
		}
	}


	// Unlock the surfaces
	UnlockSurface(bmpSurf);
	UnlockSurface(bmpFont);
}
Ejemplo n.º 4
0
ALLEGRO_BITMAP *create_bitmap_ex(int color_depth, int width, int height) {	
	SmartPointer<SDL_Surface> surf;
	if(color_depth == 8)
		surf = SDL_CreateRGBSurface(SDL_SWSURFACE, width, height, 8, 0,0,0,0);
	else
		surf = create_32bpp_sdlsurface__allegroformat(width, height);
	if(!surf.get()) {
		errors << "create_bitmap_ex: cannot create surface with " << width << "x" << height << "x" << color_depth << endl;
		return NULL;
	}
	FillSurface(surf.get(), Color());
	
	if(surf->format->BitsPerPixel != color_depth)
		warnings << "create_bitmap_ex: couldn't create surface with " << color_depth << " bpp" << endl;
	
	return create_bitmap_from_sdl(surf);
}
Ejemplo n.º 5
0
//
// CDDrawObject::UpdateAndFlipSurfaces(): Prepares the back buffer and flips.
//
HRESULT CDDrawObject::UpdateAndFlipSurfaces(void)
{
    DbgLog((LOG_TRACE, 5, TEXT("CDDrawObject::UpdateAndFlipSurfaces() entered"))) ;
    
    // Draw screen and color key on the current back buffer
    HRESULT  hr = FillSurface(m_pBackBuff) ;
    if (FAILED(hr))
    {
        DbgLog((LOG_ERROR, 1, TEXT("UpdateAndFlipSurfaces() skipped as FillSurface() failed"), hr)) ;
        return hr ;   // or return S_OK??
    }
    
    IncCount() ;                    // increment flip count first
    m_bFrontBuff = !m_bFrontBuff ;  // toggle flag
    DrawOnSurface(m_pBackBuff) ;  // draw next text on the back buffer
    
    // Keep trying to flip the buffers until successful
    while (1)
    {
        hr = m_pPrimary->Flip(NULL, 0) ;  // flip the surfaces
        if (DD_OK == hr)  // success!!
        {
            break ;
        }

        if (DDERR_SURFACELOST == hr)   // surface lost; try to restore
        {
            DbgLog((LOG_TRACE, 5, TEXT("DirectDraw surface was lost. Trying to restore..."))) ;
            hr = m_pPrimary->Restore() ;
            if (DD_OK != hr)  // couldn't restore surface
            {
                DbgLog((LOG_ERROR, 0, TEXT("IDirectDrawSurface::Restore() failed (Error 0x%lx)"), hr)) ;
                break ;
            }
        }

        if (DDERR_WASSTILLDRAWING != hr)  // some weird error -- bail out
        {
            DbgLog((LOG_ERROR, 0, TEXT("IDirectDrawSurface::Flip() failed (Error 0x%lx)"), hr)) ;
            break ;
        }
    }
    
    return hr ;
}
Ejemplo n.º 6
0
char LoadFont(const char *file) {
	char fullPath[strlen(DATA_PATH) + strlen(FONT_PATH) + strlen(file) + 1];
	sprintf(fullPath, "%s%s%s", DATA_PATH, FONT_PATH, file);
	#ifdef DEBUG
		printf("Font path = %s\n", fullPath);
	#endif
	
	// The surface holding the big long list of characters
	SDL_Surface *fontSurf = FillSurface(fullPath, 0);
	if (fontSurf == NULL) {
		return 1;
	}
	
	// For holding the rgb values for pre-rendering differently-colored
	// letters
	SDL_Color palette[256];
	uint r, g, b;
	
	// For finding the left and right side of each character (to determine
	// width)
	int leftSide;
	int rightSide;
	
	uint i = 0;
	uint transColor = SDL_MapRGB(fontSurf->format, 0xff, 0x00, 0xff);
	int sourceOffset = 0;
	for (int y = 0; y < fontSurf->h; y += FONT_H) {
		// Lock the surface (for subsequent GetPixel calls)
		SDL_LockSurface(fontSurf);
				
		/*** Determine the letter's width ***/
		// Find left side
		leftSide = -1;
		for (int x2 = 0; x2 < FONT_W; x2++) {
			for (int y2 = y; y2 < y + FONT_H; y2++) {
				if (GetPixel(fontSurf, x2, y2) != transColor) {
					leftSide = x2;
					break;
				}
			}
			if (leftSide != -1) break;
		}
		
		// Find right side
		rightSide = -1;
		for (int x2 = FONT_W - 1; x2 > 0; x2--) {
			for (int y2 = y; y2 < y + FONT_H; y2++) {
				if (GetPixel(fontSurf, x2, y2) != transColor) {
					rightSide = x2;
					break;
				}
			}
			if (rightSide != -1) break;
		}
		font[i].w = (rightSide - leftSide) + 1;
		
		SDL_UnlockSurface(fontSurf);
		
		// Blit this character and pre-render different color versions
		// of it
		for (uint j = 0; j < NUM_FONT_COLORS; j++) {
			// Prepare a surface for this color of the character
			font[i].surf[j] = MakeSurface(FONT_W, FONT_H);
			
			// Blit the temporary fontSurf onto this letter's
			// surface
			ApplySurface(-leftSide, sourceOffset, fontSurf,
			             font[i].surf[j]);
			
			// Get the rgb values for this color
			switch (j) {
				case 0: // Shadow
					r = 0;
					g = 0;
					b = 0;
					break;
				case 1: // Normal text
					r = 220;
					g = 220;
					b = 220;
					break;
				case 2: // Highlighted text
					r = 251;
					g = 177;
					b = 17;
					break;
				case 3: // Title
					r = 255;
					g = 255;
					b = 255;
			}
			
			// Set the palette of the surface
			for (uint k = 0; k < 256; k++) {
				palette[k].r = static_cast<Uint8>(r);
				palette[k].g = static_cast<Uint8>(g);
				palette[k].b = static_cast<Uint8>(b);
			}
			
			// Change the palette of the surface
			SDL_SetPalette(font[i].surf[j], SDL_LOGPAL, palette, 0,
			               256);
		}
		
		i++;
		if (i > FONT_ARRAY_SIZE - 1) break;
		
		sourceOffset -= FONT_H; // Move the big tall bmp up
	}
	
	SDL_FreeSurface(fontSurf);

	return 0;
}
Ejemplo n.º 7
0
///////////////////
// Set the video mode
bool SetVideoMode()
{
	if(bDedicated) {
		notes << "SetVideoMode: dedicated mode, ignoring" << endl;
		return true; // ignore this case
	}

	if (!tLXOptions)  {
		warnings << "SetVideoMode: Don't know what video mode to set, ignoring" << endl;
		return false;
	}

	bool resetting = false;

	// Check if already running
	if (VideoPostProcessor::videoSurface())  {
		resetting = true;
		notes << "resetting video mode" << endl;

		// seems to be a win-only problem, it works without problems here under MacOSX
#ifdef WIN32
		// using hw surfaces?
		if ((VideoPostProcessor::videoSurface()->flags & SDL_HWSURFACE) != 0) {
			warnings << "cannot change video mode because current mode uses hardware surfaces" << endl;
			// TODO: you would have to reset whole game, this is not enough!
			// The problem is in all allocated surfaces - they are hardware and when you switch
			// to window, you will most probably get software rendering
			// Also, hardware surfaces are freed from the video memory when reseting video mode
			// so you would first have to convert all surfaces to software and then perform this
			// TODO: in menu_options, restart the game also for fullscreen-change if hw surfaces are currently used
			return false;
		}
#endif
	} else {
		notes << "setting video mode" << endl;
	}

	// uninit first to ensure that the video thread is not running
	VideoPostProcessor::uninit();

	bool HardwareAcceleration = false;
	int DoubleBuf = false;
	int vidflags = 0;

	// it is faster with doublebuffering in hardware accelerated mode
	// also, it seems that it's possible that there are effects in hardware mode with double buf disabled
	// Use doublebuf when hardware accelerated
	if (HardwareAcceleration)
		DoubleBuf = true;

	// Check that the bpp is valid
	switch (tLXOptions->iColourDepth) {
	case 0:
	case 16:
	case 24:
	case 32:
		break;
	default: tLXOptions->iColourDepth = 16;
	}
	notes << "ColorDepth: " << tLXOptions->iColourDepth << endl;

	// BlueBeret's addition (2007): OpenGL support
	bool opengl = tLXOptions->bOpenGL;

	// Initialize the video
	if(tLXOptions->bFullscreen)  {
		vidflags |= SDL_FULLSCREEN;
	}

	if (opengl) {
		vidflags |= SDL_OPENGL;
#ifndef REAL_OPENGL
		vidflags |= SDL_OPENGLBLIT; // SDL will behave like normally
#endif
		// HINT: it seems that with OGL activated, SDL_SetVideoMode will already set the OGL depth size
		// though this main pixel format of the screen surface was always 32 bit for me in OGL under MacOSX
		//#ifndef MACOSX
		/*
		 short colorbitsize = (tLXOptions->iColourDepth==16) ? 5 : 8;
		 SDL_GL_SetAttribute (SDL_GL_RED_SIZE,   colorbitsize);
		 SDL_GL_SetAttribute (SDL_GL_GREEN_SIZE, colorbitsize);
		 SDL_GL_SetAttribute (SDL_GL_BLUE_SIZE,  colorbitsize);
		 SDL_GL_SetAttribute (SDL_GL_ALPHA_SIZE, colorbitsize);
		 //SDL_GL_SetAttribute (SDL_GL_DEPTH_SIZE, tLXOptions->iColourDepth);
		 */
		//#endif
		//SDL_GL_SetAttribute (SDL_GL_ALPHA_SIZE,  8);
		//SDL_GL_SetAttribute (SDL_GL_DEPTH_SIZE, 24);
		//SDL_GL_SetAttribute (SDL_GL_BUFFER_SIZE, 32);
		SDL_GL_SetAttribute (SDL_GL_DOUBLEBUFFER, 1); // always use double buffering in OGL mode
	}	

	if(HardwareAcceleration)  {
		vidflags |= SDL_HWSURFACE | SDL_HWPALETTE | SDL_HWACCEL;
		// Most (perhaps all) systems use software drawing for their stuff (windows etc.)
		// Because of that we cannot have hardware accelerated support in window - OS screen
		// is software surface. How would you make the window hardware, if it's on the screen?
		// Anyway, SDL takes care of this by istelf and disables the flag when needed
		iSurfaceFormat = SDL_HWSURFACE;
	}
	else  {
		vidflags |= SDL_SWSURFACE;
		iSurfaceFormat = SDL_SWSURFACE;
	}

	if(DoubleBuf && !opengl)
		vidflags |= SDL_DOUBLEBUF;

#ifdef WIN32
	UnSubclassWindow();  // Unsubclass before doing anything with the window
#endif


#ifdef WIN32
	// Reset the video subsystem under WIN32, else we get a "Could not reset OpenGL context" error when switching mode
	if (opengl && tLX)  {  // Don't reset when we're setting up the mode for first time (OpenLieroX not yet initialized)
		SDL_QuitSubSystem(SDL_INIT_VIDEO);
		SDL_InitSubSystem(SDL_INIT_VIDEO);
	}
#endif

	VideoPostProcessor::init();
	int scrW = VideoPostProcessor::get()->screenWidth();
	int scrH = VideoPostProcessor::get()->screenHeight();
setvideomode:
	if( SDL_SetVideoMode(scrW, scrH, tLXOptions->iColourDepth, vidflags) == NULL) {
		if (resetting)  {
			errors << "Failed to reset video mode"
					<< " (ErrorMsg: " << SDL_GetError() << "),"
					<< " let's wait a bit and retry" << endl;
			SDL_Delay(500);
			resetting = false;
			goto setvideomode;
		}

		if(tLXOptions->iColourDepth != 0) {
			errors << "Failed to use " << tLXOptions->iColourDepth << " bpp"
					<< " (ErrorMsg: " << SDL_GetError() << "),"
					<< " trying automatic bpp detection ..." << endl;
			tLXOptions->iColourDepth = 0;
			goto setvideomode;
		}

		if(vidflags & SDL_OPENGL) {
			errors << "Failed to use OpenGL"
					<< " (ErrorMsg: " << SDL_GetError() << "),"
					<< " trying without ..." << endl;
			vidflags &= ~(SDL_OPENGL | SDL_OPENGLBLIT | SDL_HWSURFACE | SDL_HWPALETTE | SDL_HWACCEL);
			goto setvideomode;
		}
		
		if(vidflags & SDL_FULLSCREEN) {
			errors << "Failed to set full screen video mode "
					<< scrW << "x" << scrH << "x" << tLXOptions->iColourDepth
					<< " (ErrorMsg: " << SDL_GetError() << "),"
					<< " trying window mode ..." << endl;
			vidflags &= ~SDL_FULLSCREEN;
			goto setvideomode;
		}

		SystemError("Failed to set the video mode " + itoa(scrW) + "x" + itoa(scrH) + "x" + itoa(tLXOptions->iColourDepth) + "\nErrorMsg: " + std::string(SDL_GetError()));
		return false;
	}

	SDL_WM_SetCaption(GetGameVersion().asHumanString().c_str(),NULL);
	SDL_ShowCursor(SDL_DISABLE);

#ifdef WIN32
	// Hint: Reset the mouse state - this should avoid the mouse staying pressed
	GetMouse()->Button = 0;
	GetMouse()->Down = 0;
	GetMouse()->FirstDown = 0;
	GetMouse()->Up = 0;

	if (!tLXOptions->bFullscreen)  {
		SubclassWindow();
	}
#endif

	// Set the change mode flag
	if (tLX)
		tLX->bVideoModeChanged = true;
	
#ifdef REAL_OPENGL	
	if((SDL_GetVideoSurface()->flags & SDL_OPENGL)) {
		static SDL_PixelFormat OGL_format32 =
		{
			NULL, //SDL_Palette *palette;
			32, //Uint8  BitsPerPixel;
			4, //Uint8  BytesPerPixel;
			0, 0, 0, 0, //Uint8  Rloss, Gloss, Bloss, Aloss;
#if SDL_BYTEORDER == SDL_LIL_ENDIAN /* OpenGL RGBA masks */
			0, 8, 16, 24, //Uint8  Rshift, Gshift, Bshift, Ashift;
			0x000000FF,
			0x0000FF00,
			0x00FF0000,
			0xFF000000,
#else
			24, 16, 8, 0, //Uint8  Rshift, Gshift, Bshift, Ashift;
			0xFF000000,
			0x00FF0000,
			0x0000FF00,
			0x000000FF,
#endif
			0, //Uint32 colorkey;
			255 //Uint8  alpha;
		};
		// some GFX stuff in OLX seems very slow when this is used
		// (probably the blit from alpha surf to this format is slow)
	/*	static SDL_PixelFormat OGL_format24 =
		{
			NULL, //SDL_Palette *palette;
			24, //Uint8  BitsPerPixel;
			3, //Uint8  BytesPerPixel;
			0, 0, 0, 0, //Uint8  Rloss, Gloss, Bloss, Aloss;
			#if SDL_BYTEORDER == SDL_LIL_ENDIAN // OpenGL RGBA masks
			0, 8, 16, 0, //Uint8  Rshift, Gshift, Bshift, Ashift;
			0x000000FF,
			0x0000FF00,
			0x00FF0000,
			0x00000000,
			#else
			16, 8, 0, 0, //Uint8  Rshift, Gshift, Bshift, Ashift;
			0x00FF0000,
			0x0000FF00,
			0x000000FF,
			0x00000000,
			#endif
			0, //Uint32 colorkey;
			255 //Uint8  alpha;
		}; */
		//if(tLXOptions->iColourDepth == 32)
			mainPixelFormat = &OGL_format32;
		//else
		//	mainPixelFormat = &OGL_format24;
	} else
#endif		
		mainPixelFormat = SDL_GetVideoSurface()->format;
	DumpPixelFormat(mainPixelFormat);
	if(SDL_GetVideoSurface()->flags & SDL_DOUBLEBUF)
		notes << "using doublebuffering" << endl;

	// Correct the surface format according to SDL
#ifdef REAL_OPENGL
	if(((SDL_GetVideoSurface()->flags & SDL_OPENGL) != 0)) {
		iSurfaceFormat = SDL_SWSURFACE;
	} else
#endif	
	if((SDL_GetVideoSurface()->flags & SDL_HWSURFACE) != 0)  {
		iSurfaceFormat = SDL_HWSURFACE;
		notes << "using hardware surfaces" << endl;
	}
	else {
		iSurfaceFormat = SDL_SWSURFACE; // HINT: under MacOSX, it doesn't seem to make any difference in performance
		if (HardwareAcceleration)
			hints << "Unable to use hardware surfaces, falling back to software." << endl;
		notes << "using software surfaces" << endl;
	}

	if(SDL_GetVideoSurface()->flags & SDL_OPENGL) {
		hints << "using OpenGL" << endl;
		
#ifdef REAL_OPENGL
		OGL_init();
#else
		FillSurface(SDL_GetVideoSurface(), Color(0, 0, 0));		
#endif
	}
	else
		FillSurface(SDL_GetVideoSurface(), Color(0, 0, 0));
	
	VideoPostProcessor::get()->resetVideo();

	notes << "video mode was set successfully" << endl;
	return true;
}
ThebesLayerBuffer::PaintState
ContentClientIncremental::BeginPaintBuffer(ThebesLayer* aLayer,
                                           ThebesLayerBuffer::ContentType aContentType,
                                           uint32_t aFlags)
{
  mTextureInfo.mDeprecatedTextureHostFlags = 0;
  PaintState result;
  // We need to disable rotation if we're going to be resampled when
  // drawing, because we might sample across the rotation boundary.
  bool canHaveRotation =  !(aFlags & ThebesLayerBuffer::PAINT_WILL_RESAMPLE);

  nsIntRegion validRegion = aLayer->GetValidRegion();

  Layer::SurfaceMode mode;
  ContentType contentType;
  nsIntRegion neededRegion;
  bool canReuseBuffer;
  nsIntRect destBufferRect;

  while (true) {
    mode = aLayer->GetSurfaceMode();
    contentType = aContentType;
    neededRegion = aLayer->GetVisibleRegion();
    // If we're going to resample, we need a buffer that's in clamp mode.
    canReuseBuffer = neededRegion.GetBounds().Size() <= mBufferRect.Size() &&
      mHasBuffer &&
      (!(aFlags & ThebesLayerBuffer::PAINT_WILL_RESAMPLE) ||
       !(mTextureInfo.mTextureFlags & TEXTURE_ALLOW_REPEAT));

    if (canReuseBuffer) {
      if (mBufferRect.Contains(neededRegion.GetBounds())) {
        // We don't need to adjust mBufferRect.
        destBufferRect = mBufferRect;
      } else {
        // The buffer's big enough but doesn't contain everything that's
        // going to be visible. We'll move it.
        destBufferRect = nsIntRect(neededRegion.GetBounds().TopLeft(), mBufferRect.Size());
      }
    } else {
      destBufferRect = neededRegion.GetBounds();
    }

    if (mode == Layer::SURFACE_COMPONENT_ALPHA) {
      if (!gfxPlatform::ComponentAlphaEnabled() ||
          !aLayer->GetParent() ||
          !aLayer->GetParent()->SupportsComponentAlphaChildren()) {
        mode = Layer::SURFACE_SINGLE_CHANNEL_ALPHA;
      } else {
        contentType = gfxASurface::CONTENT_COLOR;
      }
    }

    if ((aFlags & ThebesLayerBuffer::PAINT_WILL_RESAMPLE) &&
        (!neededRegion.GetBounds().IsEqualInterior(destBufferRect) ||
         neededRegion.GetNumRects() > 1)) {
      // The area we add to neededRegion might not be painted opaquely
      if (mode == Layer::SURFACE_OPAQUE) {
        contentType = gfxASurface::CONTENT_COLOR_ALPHA;
        mode = Layer::SURFACE_SINGLE_CHANNEL_ALPHA;
      }
      // For component alpha layers, we leave contentType as CONTENT_COLOR.

      // We need to validate the entire buffer, to make sure that only valid
      // pixels are sampled
      neededRegion = destBufferRect;
    }

    if (mHasBuffer &&
        (mContentType != contentType ||
         (mode == Layer::SURFACE_COMPONENT_ALPHA) != mHasBufferOnWhite)) {
      // We're effectively clearing the valid region, so we need to draw
      // the entire needed region now.
      result.mRegionToInvalidate = aLayer->GetValidRegion();
      validRegion.SetEmpty();
      mHasBuffer = false;
      mHasBufferOnWhite = false;
      mBufferRect.SetRect(0, 0, 0, 0);
      mBufferRotation.MoveTo(0, 0);
      // Restart decision process with the cleared buffer. We can only go
      // around the loop one more iteration, since mTexImage is null now.
      continue;
    }

    break;
  }

  result.mRegionToDraw.Sub(neededRegion, validRegion);
  if (result.mRegionToDraw.IsEmpty())
    return result;

  if (destBufferRect.width > mForwarder->GetMaxTextureSize() ||
      destBufferRect.height > mForwarder->GetMaxTextureSize()) {
    return result;
  }

  // BlitTextureImage depends on the FBO texture target being
  // TEXTURE_2D.  This isn't the case on some older X1600-era Radeons.
  if (!mForwarder->SupportsTextureBlitting() ||
      !mForwarder->SupportsPartialUploads()) {
    result.mRegionToDraw = neededRegion;
    validRegion.SetEmpty();
    mHasBuffer = false;
    mHasBufferOnWhite = false;
    mBufferRect.SetRect(0, 0, 0, 0);
    mBufferRotation.MoveTo(0, 0);
    canReuseBuffer = false;
  }

  nsIntRect drawBounds = result.mRegionToDraw.GetBounds();
  bool createdBuffer = false;

  uint32_t bufferFlags = canHaveRotation ? TEXTURE_ALLOW_REPEAT : 0;
  if (mode == Layer::SURFACE_COMPONENT_ALPHA) {
    bufferFlags |= TEXTURE_COMPONENT_ALPHA;
  }
  if (canReuseBuffer) {
    nsIntRect keepArea;
    if (keepArea.IntersectRect(destBufferRect, mBufferRect)) {
      // Set mBufferRotation so that the pixels currently in mBuffer
      // will still be rendered in the right place when mBufferRect
      // changes to destBufferRect.
      nsIntPoint newRotation = mBufferRotation +
        (destBufferRect.TopLeft() - mBufferRect.TopLeft());
      WrapRotationAxis(&newRotation.x, mBufferRect.width);
      WrapRotationAxis(&newRotation.y, mBufferRect.height);
      NS_ASSERTION(nsIntRect(nsIntPoint(0,0), mBufferRect.Size()).Contains(newRotation),
                   "newRotation out of bounds");
      int32_t xBoundary = destBufferRect.XMost() - newRotation.x;
      int32_t yBoundary = destBufferRect.YMost() - newRotation.y;
      if ((drawBounds.x < xBoundary && xBoundary < drawBounds.XMost()) ||
          (drawBounds.y < yBoundary && yBoundary < drawBounds.YMost()) ||
          (newRotation != nsIntPoint(0,0) && !canHaveRotation)) {
        // The stuff we need to redraw will wrap around an edge of the
        // buffer, so we will need to do a self-copy
        // If mBufferRotation == nsIntPoint(0,0) we could do a real
        // self-copy but we're not going to do that in GL yet.
        // We can't do a real self-copy because the buffer is rotated.
        // So allocate a new buffer for the destination.
        destBufferRect = neededRegion.GetBounds();
        createdBuffer = true;
      } else {
        mBufferRect = destBufferRect;
        mBufferRotation = newRotation;
      }
    } else {
      // No pixels are going to be kept. The whole visible region
      // will be redrawn, so we don't need to copy anything, so we don't
      // set destBuffer.
      mBufferRect = destBufferRect;
      mBufferRotation = nsIntPoint(0,0);
    }
  } else {
    // The buffer's not big enough, so allocate a new one
    createdBuffer = true;
  }
  NS_ASSERTION(!(aFlags & ThebesLayerBuffer::PAINT_WILL_RESAMPLE) ||
               destBufferRect == neededRegion.GetBounds(),
               "If we're resampling, we need to validate the entire buffer");

  if (!createdBuffer && !mHasBuffer) {
    return result;
  }

  if (createdBuffer) {
    if (mHasBuffer &&
        (mode != Layer::SURFACE_COMPONENT_ALPHA || mHasBufferOnWhite)) {
      mTextureInfo.mDeprecatedTextureHostFlags = TEXTURE_HOST_COPY_PREVIOUS;
    }

    mHasBuffer = true;
    if (mode == Layer::SURFACE_COMPONENT_ALPHA) {
      mHasBufferOnWhite = true;
    }
    mBufferRect = destBufferRect;
    mBufferRotation = nsIntPoint(0,0);
    NotifyBufferCreated(contentType, bufferFlags);
  }

  NS_ASSERTION(canHaveRotation || mBufferRotation == nsIntPoint(0,0),
               "Rotation disabled, but we have nonzero rotation?");

  nsIntRegion invalidate;
  invalidate.Sub(aLayer->GetValidRegion(), destBufferRect);
  result.mRegionToInvalidate.Or(result.mRegionToInvalidate, invalidate);

  // BeginUpdate is allowed to modify the given region,
  // if it wants more to be repainted than we request.
  if (mode == Layer::SURFACE_COMPONENT_ALPHA) {
    nsIntRegion drawRegionCopy = result.mRegionToDraw;
    nsRefPtr<gfxASurface> onBlack = GetUpdateSurface(BUFFER_BLACK, drawRegionCopy);
    nsRefPtr<gfxASurface> onWhite = GetUpdateSurface(BUFFER_WHITE, result.mRegionToDraw);
    if (onBlack && onWhite) {
      NS_ASSERTION(result.mRegionToDraw == drawRegionCopy,
                   "BeginUpdate should always modify the draw region in the same way!");
      FillSurface(onBlack, result.mRegionToDraw, nsIntPoint(drawBounds.x, drawBounds.y), gfxRGBA(0.0, 0.0, 0.0, 1.0));
      FillSurface(onWhite, result.mRegionToDraw, nsIntPoint(drawBounds.x, drawBounds.y), gfxRGBA(1.0, 1.0, 1.0, 1.0));
      if (RefPtr<DrawTarget> onBlackDT = gfxPlatform::GetPlatform()->CreateDrawTargetForUpdateSurface(onBlack, onBlack->GetSize())) {
        RefPtr<DrawTarget> onWhiteDT = gfxPlatform::GetPlatform()->CreateDrawTargetForUpdateSurface(onWhite, onWhite->GetSize());
        RefPtr<DrawTarget> dt = Factory::CreateDualDrawTarget(onBlackDT, onWhiteDT);
        result.mContext = new gfxContext(dt);
      } else {
        gfxASurface* surfaces[2] = { onBlack.get(), onWhite.get() };
        nsRefPtr<gfxTeeSurface> surf = new gfxTeeSurface(surfaces, ArrayLength(surfaces));

        // XXX If the device offset is set on the individual surfaces instead of on
        // the tee surface, we render in the wrong place. Why?
        gfxPoint deviceOffset = onBlack->GetDeviceOffset();
        onBlack->SetDeviceOffset(gfxPoint(0, 0));
        onWhite->SetDeviceOffset(gfxPoint(0, 0));
        surf->SetDeviceOffset(deviceOffset);

        // Using this surface as a source will likely go horribly wrong, since
        // only the onBlack surface will really be used, so alpha information will
        // be incorrect.
        surf->SetAllowUseAsSource(false);
        result.mContext = new gfxContext(surf);
      }
    } else {
      result.mContext = nullptr;
    }
  } else {
    nsRefPtr<gfxASurface> surf = GetUpdateSurface(BUFFER_BLACK, result.mRegionToDraw);
    if (RefPtr<DrawTarget> dt = gfxPlatform::GetPlatform()->CreateDrawTargetForUpdateSurface(surf, surf->GetSize())) {
      result.mContext = new gfxContext(dt);
    } else {
      result.mContext = new gfxContext(surf);
    }
  }
  if (!result.mContext) {
    NS_WARNING("unable to get context for update");
    return result;
  }
  result.mContext->Translate(-gfxPoint(drawBounds.x, drawBounds.y));

  // If we do partial updates, we have to clip drawing to the regionToDraw.
  // If we don't clip, background images will be fillrect'd to the region correctly,
  // while text or lines will paint outside of the regionToDraw. This becomes apparent
  // with concave regions. Right now the scrollbars invalidate a narrow strip of the bar
  // although they never cover it. This leads to two draw rects, the narow strip and the actually
  // newly exposed area. It would be wise to fix this glitch in any way to have simpler
  // clip and draw regions.
  gfxUtils::ClipToRegion(result.mContext, result.mRegionToDraw);

  if (mContentType == gfxASurface::CONTENT_COLOR_ALPHA) {
    result.mContext->SetOperator(gfxContext::OPERATOR_CLEAR);
    result.mContext->Paint();
    result.mContext->SetOperator(gfxContext::OPERATOR_OVER);
  }

  return result;
}