Continuando nossa série de artigos traduzidos da lazyfoo, veremos agora como trabalhar com texturas cuja fonte não seja uma imagem bitmap.
Algumas vezes queremos renderizar os dados de um pixel de uma origem que não seja um bitmap, como de uma webcam. Usando a transmissão de texturas, podemos renderizar pixels de qualquer origem.
//Texture wrapper class class LTexture { public: //Initializes variables LTexture(); //Deallocates memory ~LTexture(); //Loads image at specified path bool loadFromFile( std::string path ); #ifdef _SDL_TTF_H //Creates image from font string bool loadFromRenderedText( std::string textureText, SDL_Color textColor ); #endif //Creates blank texture bool createBlank( int width, int height ); //Deallocates texture void free(); //Set color modulation void setColor( Uint8 red, Uint8 green, Uint8 blue ); //Set blending void setBlendMode( SDL_BlendMode blending ); //Set alpha modulation void setAlpha( Uint8 alpha ); //Renders texture at given point void render( int x, int y, SDL_Rect* clip = NULL, double angle = 0.0, SDL_Point* center = NULL, SDL_RendererFlip flip = SDL_FLIP_NONE ); //Gets image dimensions int getWidth(); int getHeight(); //Pixel manipulators bool lockTexture(); bool unlockTexture(); void* getPixels(); void copyPixels( void* pixels ); int getPitch(); Uint32 getPixel32( unsigned int x, unsigned int y ); private: //The actual hardware texture SDL_Texture* mTexture; void* mPixels; int mPitch; //Image dimensions int mWidth; int mHeight; };
Aqui nós adicionamos mais funcionalidade à nossa classe de textura. A função createBlank aloca uma textura em branco onde podemos copiar dados quando estivermos transmitindo. A função copyPixels copia os dados dos pixel que queremos transmitir.
//A test animation stream class DataStream { public: //Initializes internals DataStream(); //Loads initial data bool loadMedia(); //Deallocator void free(); //Gets current frame data void* getBuffer(); private: //Internal data SDL_Surface* mImages[ 4 ]; int mCurrentImage; int mDelayFrames; };
Aqui temos nossa classe de transmissão de dados. Não nos aprofundaremos em como ela funciona pois não nos importamos com ela. Quando se lida com API’s de webcam, decodificação de vídeo, tipicamente não se aprofunda muito em como elas funcionam pois tudo que nos importamos é obter os dados de vídeo ou áudio deles.
Tudo que nos importamos aqui é a função getBuffer, que lê os pixels atuais do buffer de dados.
bool LTexture::createBlank( int width, int height ) { //Create uninitialized texture mTexture = SDL_CreateTexture( gRenderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_STREAMING, width, height ); if( mTexture == NULL ) { printf( "Unable to create blank texture! SDL Error: %s\n", SDL_GetError() ); } else { mWidth = width; mHeight = height; } return mTexture != NULL; }
Como você pode ver, tudo que essa função faz é criar um textura RGBA de 32 bits com acesso para transmissão. Uma coisa que você tem que se certificar é de que quando criar sua textura o formato dela combine com o formato dos pixels que estamos transmitindo.
void LTexture::copyPixels( void* pixels ) { //Texture is locked if( mPixels != NULL ) { //Copy to locked pixels memcpy( mPixels, pixels, mPitch * mHeight ); } }
Aqui temos nossa função que cópia os pixels sendo transmitidos. A função assume que a textura está bloqueada e que os pixels são de uma imagem do mesmo tamanho da textura.
//While application is running while( !quit ) { //Handle events on queue while( SDL_PollEvent( &e ) != 0 ) { //User requests quit if( e.type == SDL_QUIT ) { quit = true; } } //Clear screen SDL_SetRenderDrawColor( gRenderer, 0xFF, 0xFF, 0xFF, 0xFF ); SDL_RenderClear( gRenderer ); //Copy frame from buffer gStreamingTexture.lockTexture(); gStreamingTexture.copyPixels( gDataStream.getBuffer() ); gStreamingTexture.unlockTexture(); //Render frame gStreamingTexture.render( ( SCREEN_WIDTH - gStreamingTexture.getWidth() ) / 2, ( SCREEN_HEIGHT - gStreamingTexture.getHeight() ) / 2 ); //Update screen SDL_RenderPresent( gRenderer ); }
Na renderização feito no loop principal, bloqueamos nossa transmissão de textura, copiamos os pixels da transmissão e então desbloqueamos a textura. Com a nossa textura guardando a imagem mais recente da transmissão, renderizamos a imagem na tela. Quando lidamos com APIs de decodificação, as coisas podem ficar mais difíceis, pois talvez teríamos que converter de um formato para outro, mas aqui tudo que temos que fazer é ler os dados dos pixels e copia-los para a tela.
Baixe os arquivos de mídia e de código fonte desse artigo aqui.