Jump to content
  • Advertisement
Sign in to follow this  
kopiluwak

"special" effects with GDI

This topic is 3443 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hi there, recently I stumbled across an old C64 magazine which wrote about an effect called FLD (flexible line distance). From what I understood this effect allows to draw each pixel-line of a text at a arbitrary y-position. I know that this no problem anymore, but I was wondering how you would do this on a 'normal' window. My first guess was creating compatible bitmap with the target-DC length an the height of the text and then BitBlt each line at the desired position, but is the way to do this? Or is there a better, more elegant way? Thanks for your time. JKF

Share this post


Link to post
Share on other sites
Advertisement
I tried myself at an implementation. Here is the result perhaps it's usefull to anyone

FLD_effect.h
#pragma once

class FLD_effect
{
public:
FLD_effect(void);
virtual ~FLD_effect(void);

virtual void init(HWND target_hwnd, int x_coord, int y_coord, int width, int height, char* p_text);

virtual void step();
virtual void draw();
virtual int get_next_char();
virtual void release();
virtual void update();
DWORD get_sample_value(char* p_control_string);
void calc_remaining_width();
void calc_lower_x_bound();

HWND m_hwnd;
HDC m_main_dc;
HDC m_anim_dc;
HBITMAP m_anim_bmp;
char* m_animation_text;
char* m_current_text_pos;
DWORD m_animation_text_length;
DWORD m_row_count;
DWORD m_effect_width;
DWORD m_remaining_width;
DWORD m_effect_height;
int m_x_coordinate;
int m_y_offset;
int m_lower_x_bound;

};




FLD_effect.cpp
#include "FLD_effect.h"
#include <math.h>
#include <stdlib.h>


int backbuffer_width = 0;
int backbuffer_height = 17;

#define BACKBUFFER_WIDTH backbuffer_width
#define BACKBUFFER_HEIGHT backbuffer_height

#define Y_COORD_LIST_SIZE 150
int y_coord_list[ Y_COORD_LIST_SIZE ];


FLD_effect::FLD_effect(void)
{
}

FLD_effect::~FLD_effect(void)
{
}


void FLD_effect::init(HWND target_hwnd, int x_coord, int y_coord, int width, int height, char* p_text)
{
//unnecessary long value for pi, just copied from calc
const double pi = 3.1415926535897932384626433832795;
//we want two periods of the sine and fitting end and start
const double step_width = 4*pi / Y_COORD_LIST_SIZE;
double step = 0.0;

//initialize the correct width for the backbuffer
backbuffer_width = width*2;

//create a source device context from where we get the single lines of the text.
m_hwnd = target_hwnd;
m_main_dc = GetDC( m_hwnd );
m_anim_dc = CreateCompatibleDC( m_main_dc );
m_anim_bmp = CreateCompatibleBitmap(m_anim_dc, BACKBUFFER_WIDTH, BACKBUFFER_HEIGHT);
SelectObject(m_anim_dc, m_anim_bmp);

//initialize the text, normally you would set this value to something else when initializing the effect.
m_animation_text = p_text;
m_animation_text_length = strlen(m_animation_text);

//fill the source buffer
BitBlt(m_anim_dc, 0,0, BACKBUFFER_WIDTH, BACKBUFFER_HEIGHT, NULL, 0, 0, WHITENESS);
TextOut(m_anim_dc,0,0,m_animation_text, m_animation_text_length);

//set the row count, so we know the number of rows of which the text consists
m_row_count = BACKBUFFER_HEIGHT;

//set the area for the effect on the window. This all should be incorporated in a
//dedicated window but I can't be bothered at the moment.
m_effect_width = width;
m_effect_height = height;
m_remaining_width = m_effect_width;
m_x_coordinate = m_effect_width;
m_y_offset = y_coord;

//initialize the current pointer to the start of the text.
m_current_text_pos = m_animation_text;

calc_lower_x_bound();

//fill the y offsets for the effect
for(DWORD index = 0; index < Y_COORD_LIST_SIZE; ++index)
{
y_coord_list[ index ] = (int) ( -( (double)m_effect_height )* 0.40 * sin( step ));
step += step_width;
}

}

void FLD_effect::step()
{
RECT rect;

rect.top = m_y_offset - m_effect_height / 2;
rect.bottom = m_y_offset + m_effect_height / 2;
rect.left = 0;
rect.right = m_effect_width;

InvalidateRect(m_hwnd, &rect, FALSE);
}

void FLD_effect::draw()
{
static int y_base_index = 0;
int y_coord_index = 0;
DWORD y_coordinate = 0;

//clear the background
BitBlt( m_main_dc, 0, m_y_offset - m_effect_height/2, m_effect_width, m_effect_height, m_anim_dc, 0, 0, WHITENESS);

//draw each line at its respective x- and y-offset
for(DWORD row_index = 0; row_index < m_row_count-1; ++row_index)
{
y_coord_index = (y_base_index + row_index) % Y_COORD_LIST_SIZE;
y_coordinate = y_coord_list[ y_coord_index ] + m_y_offset;
//use transparent blit to preserve the background
::TransparentBlt( m_main_dc, m_x_coordinate, y_coordinate, m_remaining_width, 1, m_anim_dc, 0, row_index+1, m_remaining_width, 1, 0x00FFFFFF);
}

//increment the base offset into the y coordinate buffer
++y_base_index;

//check whether the current character is already outside the visible area
if( (0 > m_x_coordinate) && ( -m_x_coordinate > m_lower_x_bound ) )
{
//since the get_next_char method will move the source buffer one character further
//in the device context we need to move the x coordinate accordingly.
get_next_char();
calc_remaining_width();
}
else
{
--m_x_coordinate;
}
}

int FLD_effect::get_next_char()
{
int return_value = 0;
int text_len = 0;
RECT rect ={0};

if( 0 != (*m_current_text_pos) )
{
m_current_text_pos++;
if( 0 == (*m_current_text_pos) )
{
m_current_text_pos = m_animation_text;
}
else
{
//DO NOTHING HERE
}
}
else
{
m_current_text_pos = m_animation_text;
}

//in every case update the text length so any change in the string is handled
m_animation_text_length = strlen( m_current_text_pos );

//get the size of the first character
DrawText(m_anim_dc, m_current_text_pos, 1, &rect, DT_CALCRECT);

//put the fitting text snippet in the backbuffer
TextOut(m_anim_dc, 0, 0, m_current_text_pos, m_animation_text_length);

calc_remaining_width();

//move the x coordinate to the next character
m_x_coordinate += m_lower_x_bound;
//calculate the width of the current character
calc_lower_x_bound();

if( m_current_text_pos == m_animation_text )
{
m_x_coordinate = m_effect_width;
}
else
{
//DO NOTHING HERE
}

//return the widht of the first character displayed
return return_value;
}

void FLD_effect::calc_remaining_width()
{
RECT remain_rect;

//get the length in pixels of the remaining text string
DrawText(m_anim_dc, m_current_text_pos, m_animation_text_length, &remain_rect, DT_CALCRECT);

m_remaining_width = remain_rect.right - remain_rect.left;
m_remaining_width %= (m_effect_width + 1);
}

DWORD FLD_effect::get_sample_value(char* p_control_string)
{
int offset = 0;
int value = 0;
int total = 0;
unsigned char sample_value = 0;
DWORD sample_combined = 0;
int text_len = strlen( p_control_string );
DWORD color_ref = 0;
DWORD xor_value = 0;
char* p_end = NULL;

if(10 <= text_len )
{
xor_value = strtoul( &(p_control_string[text_len - 8]), &p_end, 16);
text_len -= 8;
}
else
{
//DO NOTHING HERE
}

text_len /= 2;

for(int index = 0; index < text_len; ++index)
{
offset = *p_control_string;
offset %= m_effect_width;
++p_control_string;
value = *p_control_string;
++p_control_string;

sample_combined = 0;
for(int sample_divisor = 1; sample_divisor < 5; ++sample_divisor)
{
sample_value = 0;

for(int line = 0 + 5; line < 8 + 5; ++line)
{
color_ref = GetPixel(m_anim_dc, offset/4 + 5*sample_divisor, line);

sample_value <<= 1;

if( 0 == color_ref )
{
sample_value |= 1;
}
else
{
//DO NOTHING HERE
}
}
sample_combined <<= 8;
sample_combined |= sample_value;
}

sample_combined *= y_coord_list[value % (sizeof(y_coord_list)/sizeof(y_coord_list[0]))];

total += sample_combined;
}

total ^= xor_value;

return total;
}

void FLD_effect::release()
{
//clean up the mess we made
ReleaseDC( m_hwnd, m_main_dc );
DeleteDC(m_anim_dc);
DeleteObject(m_anim_bmp);

m_animation_text = "";
m_current_text_pos = NULL;
m_animation_text_length = 0;
m_row_count = 0;
m_effect_width = 0;
m_remaining_width = 0;
m_effect_height = 0;
m_x_coordinate = 0;
m_y_offset = 0;
m_lower_x_bound = 0;
}

void FLD_effect::calc_lower_x_bound()
{
RECT rect;

//get the size of the first character
DrawText(m_anim_dc, m_current_text_pos, 1, &rect, DT_CALCRECT);
//assign the new bound
m_lower_x_bound = rect.right - rect.left;;
}

void FLD_effect::update()
{
m_current_text_pos = m_animation_text;
m_animation_text_length = strlen( m_animation_text );
calc_remaining_width();
//fill the source buffer
BitBlt(m_anim_dc, 0,0, BACKBUFFER_WIDTH, BACKBUFFER_HEIGHT, NULL, 0, 0, WHITENESS);
TextOut(m_anim_dc,0,0,m_animation_text, m_animation_text_length);
calc_lower_x_bound();
}




Hints for flaws are very welcome.

JKF

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

Participate in the game development conversation and more when you create an account on GameDev.net!

Sign me up!