#include <stdio.h>
#include <string.h>
#include <stdint.h>
+#include <fcntl.h>
#include <sys/shm.h>
+#include <sys/mman.h>
#include "bcbitmap.h"
#include "bchash.h"
//static BCCounter counter;
-
-VFramePng::VFramePng(unsigned char *png_data, double scale)
+VFramePng::VFramePng(unsigned char *png_data, double s)
{
long image_size =
((long)png_data[0] << 24) | ((long)png_data[1] << 16) |
((long)png_data[2] << 8) | (long)png_data[3];
- if( !scale ) scale = BC_WindowBase::get_resources()->icon_scale;
- read_png(png_data+4, image_size, scale, scale);
+ if( !s ) s = BC_WindowBase::get_resources()->icon_scale;
+ read_png(png_data+4, image_size, s, s);
}
-VFramePng::VFramePng(unsigned char *png_data, long image_size, double xscale, double yscale)
+VFramePng::VFramePng(unsigned char *png_data, long image_size, double xs, double ys)
{
- if( !xscale ) xscale = BC_WindowBase::get_resources()->icon_scale;
- if( !yscale ) yscale = BC_WindowBase::get_resources()->icon_scale;
- read_png(png_data, image_size, xscale, yscale);
+ if( !xs ) xs = BC_WindowBase::get_resources()->icon_scale;
+ if( !ys ) ys = BC_WindowBase::get_resources()->icon_scale;
+ read_png(png_data, image_size, xs, ys);
}
VFramePng::~VFramePng()
{
}
+VFrame *VFramePng::vframe_png(int fd, double xs, double ys)
+{
+ struct stat st;
+ if( fstat(fd, &st) ) return 0;
+ long len = st.st_size;
+ if( !len ) return 0;
+ int w = 0, h = 0;
+ unsigned char *bfr = (unsigned char *)
+ ::mmap (NULL, len, PROT_READ, MAP_SHARED, fd, 0);
+ if( bfr == MAP_FAILED ) return 0;
+ VFrame *vframe = new VFramePng(bfr, len, xs, ys);
+ if( (w=vframe->get_w()) <= 0 || (h=vframe->get_h()) <= 0 ||
+ vframe->get_data() == 0 ) { delete vframe; vframe = 0; }
+ ::munmap(bfr, len);
+ return vframe;
+}
+VFrame *VFramePng::vframe_png(const char *png_path, double xs, double ys)
+{
+ VFrame *vframe = 0;
+ int fd = ::open(png_path, O_RDONLY);
+ if( fd >= 0 ) {
+ vframe = vframe_png(fd, xs, ys);
+ ::close(fd);
+ }
+ return vframe;
+}
VFrame::VFrame(VFrame &frame)
{
reset_parameters(1);
params = new BC_Hash;
+ use_shm = frame.use_shm;
allocate_data(0, -1, 0, 0, 0, frame.w, frame.h,
frame.color_model, frame.bytes_per_line);
- memcpy(data, frame.data, bytes_per_line * h);
- copy_stacks(&frame);
+ copy_vframe(&frame);
}
+
VFrame::VFrame(int w, int h, int color_model, long bytes_per_line)
{
reset_parameters(1);
+// use bytes_per_line == 0 to allocate default unshared
+ if( !bytes_per_line ) { bytes_per_line = -1; use_shm = 0; }
params = new BC_Hash;
allocate_data(data, -1, 0, 0, 0, w, h,
color_model, bytes_per_line);
sequence_number = -1;
timestamp = -1.;
is_keyframe = 0;
+ draw_point = 0;
+ pixel_rgb = 0x000000; // BLACK
+ pixel_yuv = 0x008080;
+ stipple = 0;
if(do_opengl)
{
pbuffer = 0;
}
+#ifdef LEAKER
+if( memory_type != VFrame::SHARED )
+ printf("==del %p from %p\n", data, __builtin_return_address(0));
+#endif
+
// Delete data
switch(memory_type)
{
case BC_YUV444P:
case BC_RGB_FLOATP:
case BC_RGBA_FLOATP:
+ case BC_GBRP:
break;
default:
this->u_offset = sz;
this->v_offset = sz + sz;
break;
+ case BC_GBRP:
+ if( this->v_offset ) break;
+ this->y_offset = 0;
+ this->u_offset = sz * sizeof(uint8_t);
+ this->v_offset = 2 * sz * sizeof(uint8_t);
+ break;
case BC_RGBA_FLOATP:
if( this->v_offset || a ) break;
a = this->data + 3 * sz * sizeof(float);
}
else {
memory_type = VFrame::PRIVATE;
+ this->data = 0;
int size = calculate_data_size(this->w, this->h,
this->bytes_per_line, this->color_model);
- if(BC_WindowBase::get_resources()->use_vframe_shm() && use_shm) {
+ if( use_shm && size >= SHM_MIN_SIZE &&
+ BC_WindowBase::get_resources()->use_vframe_shm() ) {
this->shmid = shmget(IPC_PRIVATE, size, IPC_CREAT | 0777);
- if(this->shmid < 0) {
- printf("VFrame::allocate_data %d could not allocate shared memory\n", __LINE__);
- }
-
- this->data = (unsigned char*)shmat(this->shmid, NULL, 0);
-//printf("VFrame::allocate_data %d %d %d\n", __LINE__, size, this->shmid);
-
-//printf("VFrame::allocate_data %d %p\n", __LINE__, this->data);
+ if( this->shmid >= 0 ) {
+ this->data = (unsigned char*)shmat(this->shmid, NULL, 0);
+//printf("VFrame::allocate_data %d %d %d %p\n", __LINE__, size, this->shmid, this->data);
// This causes it to automatically delete when the program exits.
- shmctl(this->shmid, IPC_RMID, 0);
+ shmctl(this->shmid, IPC_RMID, 0);
+ }
+ else {
+ printf("VFrame::allocate_data %d could not allocate"
+ " shared memory, %dx%d (model %d) size=0x%08x\n",
+ __LINE__, w, h, color_model, size);
+ BC_Trace::dump_shm_stats(stdout);
+ }
}
- else {
// Have to use malloc for libpng
-//printf("==vframe %d from %p\n", size, __builtin_return_address(0));
+ if( !this->data ) {
this->data = (unsigned char *)malloc(size);
+ this->shmid = -1;
}
-
// Memory check
// if(this->w * this->h > 1500 * 1100)
// printf("VFrame::allocate_data 2 this=%p w=%d h=%d this->data=%p\n",
if(!this->data)
printf("VFrame::allocate_data %dx%d: memory exhausted.\n", this->w, this->h);
+#ifdef LEAKER
+printf("==new %p from %p sz %d\n", this->data, __builtin_return_address(0), size);
+#endif
//printf("VFrame::allocate_data %d %p data=%p %d %d\n", __LINE__, this, this->data, this->w, this->h);
//if(size > 1000000) printf("VFrame::allocate_data %d\n", size);
int VFrame::write_png(const char *path)
{
+ VFrame *vframe = this;
png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0);
png_infop info_ptr = png_create_info_struct(png_ptr);
FILE *out_fd = fopen(path, "w");
- if(!out_fd)
- {
+ if(!out_fd) {
printf("VFrame::write_png %d %s %s\n", __LINE__, path, strerror(errno));
return 1;
}
int png_cmodel = PNG_COLOR_TYPE_RGB;
- switch(get_color_model())
- {
- case BC_RGB888:
- case BC_YUV888:
- png_cmodel = PNG_COLOR_TYPE_RGB;
- break;
-
- case BC_RGBA8888:
- case BC_YUVA8888:
+ int bc_cmodel = get_color_model();
+ switch( bc_cmodel ) {
+ case BC_RGB888: break;
+ case BC_RGBA8888: png_cmodel = PNG_COLOR_TYPE_RGB_ALPHA; break;
+ case BC_A8: png_cmodel = PNG_COLOR_TYPE_GRAY; break;
+ default:
+ bc_cmodel = BC_RGB888;
+ if( BC_CModels::has_alpha(bc_cmodel) ) {
+ bc_cmodel = BC_RGBA8888;
png_cmodel = PNG_COLOR_TYPE_RGB_ALPHA;
- break;
-
- case BC_A8:
- png_cmodel = PNG_COLOR_TYPE_GRAY;
- break;
+ }
+ vframe = new VFrame(get_w(), get_h(), bc_cmodel, 0);
+ vframe->transfer_from(this);
+ break;
}
-
png_init_io(png_ptr, out_fd);
png_set_compression_level(png_ptr, 9);
- png_set_IHDR(png_ptr,
- info_ptr,
- get_w(),
- get_h(),
- 8,
- png_cmodel,
- PNG_INTERLACE_NONE,
- PNG_COMPRESSION_TYPE_DEFAULT,
- PNG_FILTER_TYPE_DEFAULT);
+ png_set_IHDR(png_ptr, info_ptr, get_w(), get_h(), 8, png_cmodel,
+ PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
png_write_info(png_ptr, info_ptr);
- png_write_image(png_ptr, get_rows());
+ png_write_image(png_ptr, vframe->get_rows());
png_write_end(png_ptr, info_ptr);
png_destroy_write_struct(&png_ptr, &info_ptr);
fclose(out_fd);
+ if( vframe != this ) delete vframe;
return 0;
}
case BC_YUV410P:
bzero(get_y(), sz);
- bzero(get_u(), w / 4 * h / 4);
- bzero(get_v(), w / 4 * h / 4);
+ memset(get_u(), 0x80, w / 4 * h / 4);
+ memset(get_v(), 0x80, w / 4 * h / 4);
break;
case BC_YUV411P:
case BC_YUV420P:
case BC_YUV420PI:
bzero(get_y(), sz);
- bzero(get_u(), sz / 4);
- bzero(get_v(), sz / 4);
+ memset(get_u(), 0x80, sz / 4);
+ memset(get_v(), 0x80, sz / 4);
break;
case BC_YUV422P:
bzero(get_y(), sz);
- bzero(get_u(), sz / 2);
- bzero(get_v(), sz / 2);
+ memset(get_u(), 0x80, sz / 2);
+ memset(get_v(), 0x80, sz / 2);
+ break;
+
+ case BC_GBRP:
+ bzero(get_y(), sz);
+ bzero(get_u(), sz);
+ bzero(get_b(), sz);
break;
case BC_RGBA_FLOATP: if( a ) {
float *ap = (float *)a;
- for( int i=sz; --i>=0; ++ap ) *ap = 1.f; }
+ for( int i=sz; --i>=0; ++ap ) *ap = 0.f; }
case BC_RGB_FLOATP: {
float *rp = (float *)y;
for( int i=sz; --i>=0; ++rp ) *rp = 0.f;
break; }
case BC_YUV444P:
bzero(get_y(), sz);
- bzero(get_u(), sz);
- bzero(get_v(), sz);
+ memset(get_u(), 0x80, sz);
+ memset(get_v(), 0x80, sz);
break;
case BC_YUV888:
void VFrame::rotate90()
{
// Allocate new frame
- int new_w = h, new_h = w, new_bytes_per_line = bytes_per_pixel * new_w;
- unsigned char *new_data = new unsigned char[calculate_data_size(new_w, new_h, new_bytes_per_line, color_model)];
- unsigned char **new_rows = new unsigned char*[new_h];
- for(int i = 0; i < new_h; i++)
- new_rows[i] = &new_data[new_bytes_per_line * i];
-
+ int new_w = h, new_h = w;
+ VFrame new_frame(new_w, new_h, color_model);
+ unsigned char **new_rows = new_frame.get_rows();
// Copy data
for(int in_y = 0, out_x = new_w - 1; in_y < h; in_y++, out_x--)
{
}
// Swap frames
- clear_objects(0);
+// swap memory
+ unsigned char *new_data = new_frame.data;
+ new_frame.data = data;
data = new_data;
+// swap rows
+ new_rows = new_frame.rows;
+ new_frame.rows = rows;
rows = new_rows;
- bytes_per_line = new_bytes_per_line;
- w = new_w;
- h = new_h;
+// swap shmid
+ int new_shmid = new_frame.shmid;
+ new_frame.shmid = shmid;
+ shmid = new_shmid;
+// swap bytes_per_line
+ int new_bpl = new_frame.bytes_per_line;
+ new_frame.bytes_per_line = bytes_per_line;
+ bytes_per_line = new_bpl;
+ new_frame.clear_objects(0);
+
+ w = new_frame.w;
+ h = new_frame.h;
}
void VFrame::rotate270()
{
// Allocate new frame
- int new_w = h, new_h = w, new_bytes_per_line = bytes_per_pixel * new_w;
- unsigned char *new_data = new unsigned char[calculate_data_size(new_w, new_h, new_bytes_per_line, color_model)];
- unsigned char **new_rows = new unsigned char*[new_h];
- for(int i = 0; i < new_h; i++)
- new_rows[i] = &new_data[new_bytes_per_line * i];
-
+ int new_w = h, new_h = w;
+ VFrame new_frame(new_w, new_h, color_model);
+ unsigned char **new_rows = new_frame.get_rows();
// Copy data
for(int in_y = 0, out_x = 0; in_y < h; in_y++, out_x++)
{
}
// Swap frames
- clear_objects(0);
+// swap memory
+ unsigned char *new_data = new_frame.data;
+ new_frame.data = data;
data = new_data;
+// swap rows
+ new_rows = new_frame.rows;
+ new_frame.rows = rows;
rows = new_rows;
- bytes_per_line = new_bytes_per_line;
- w = new_w;
- h = new_h;
+// swap shmid
+ int new_shmid = new_frame.shmid;
+ new_frame.shmid = shmid;
+ shmid = new_shmid;
+// swap bytes_per_line
+ int new_bpl = new_frame.bytes_per_line;
+ new_frame.bytes_per_line = bytes_per_line;
+ bytes_per_line = new_bpl;
+ new_frame.clear_objects(0);
+
+ w = new_frame.w;
+ h = new_frame.h;
}
void VFrame::flip_vert()
{
- unsigned char *temp = new unsigned char[bytes_per_line];
- for(int i = 0, j = h - 1; i < j; i++, j--)
- {
+ unsigned char temp[bytes_per_line];
+ for( int i=0, j=h; --j>i; ++i ) {
memcpy(temp, rows[j], bytes_per_line);
memcpy(rows[j], rows[i], bytes_per_line);
memcpy(rows[i], temp, bytes_per_line);
}
- delete [] temp;
}
void VFrame::flip_horiz()
int VFrame::copy_from(VFrame *frame)
{
+ if(this->w != frame->get_w() ||
+ this->h != frame->get_h())
+ {
+ printf("VFrame::copy_from %d sizes differ src %dx%d != dst %dx%d\n",
+ __LINE__,
+ frame->get_w(),
+ frame->get_h(),
+ get_w(),
+ get_h());
+ return 1;
+ }
+
int w = MIN(this->w, frame->get_w());
int h = MIN(this->h, frame->get_h());
timestamp = frame->timestamp;
int VFrame::transfer_from(VFrame *that, int bg_color, int in_x, int in_y, int in_w, int in_h)
{
+ timestamp = that->timestamp;
+ copy_params(that);
+
if( this->get_color_model() == that->get_color_model() &&
this->get_w() == that->get_w() && this->get_h() == that->get_h() &&
this->get_bytes_per_line() == that->get_bytes_per_line() )
return this->copy_from(that);
- timestamp = that->timestamp;
#if 0
BC_CModels::transfer(
this->get_rows(), that->get_rows(), // Packed data out/in
}
-
-
-
-
-#define OVERLAY(type, max, components) \
-{ \
- type **in_rows = (type**)src->get_rows(); \
- type **out_rows = (type**)get_rows(); \
- int in_w = src->get_w(); \
- int in_h = src->get_h(); \
- \
- for(int i = 0; i < in_h; i++) \
- { \
- if(i + out_y1 >= 0 && i + out_y1 < h) \
- { \
- type *src_row = in_rows[i]; \
- type *dst_row = out_rows[i + out_y1] + out_x1 * components; \
- \
- for(int j = 0; j < in_w; j++) \
- { \
- if(j + out_x1 >= 0 && j + out_x1 < w) \
- { \
- int opacity = src_row[3]; \
- int transparency = dst_row[3] * (max - src_row[3]) / max; \
- dst_row[0] = (transparency * dst_row[0] + opacity * src_row[0]) / max; \
- dst_row[1] = (transparency * dst_row[1] + opacity * src_row[1]) / max; \
- dst_row[2] = (transparency * dst_row[2] + opacity * src_row[2]) / max; \
- dst_row[3] = MAX(dst_row[3], src_row[3]); \
- } \
- \
- dst_row += components; \
- src_row += components; \
- } \
- } \
- } \
-}
-
-
-void VFrame::overlay(VFrame *src,
- int out_x1,
- int out_y1)
-{
- switch(get_color_model())
- {
- case BC_RGBA8888:
- OVERLAY(unsigned char, 0xff, 4);
- break;
- }
-}
-
-
-
int VFrame::get_scale_tables(int *column_table, int *row_table,
int in_x1, int in_y1, int in_x2, int in_y2,
int out_x1, int out_y1, int out_x2, int out_y2)
{
clear_stacks();
- for(int i = 0; i < src->next_effects.total; i++)
- {
- char *ptr;
- next_effects.append(ptr = new char[strlen(src->next_effects.values[i]) + 1]);
- strcpy(ptr, src->next_effects.values[i]);
- }
- for(int i = 0; i < src->prev_effects.total; i++)
- {
- char *ptr;
- prev_effects.append(ptr = new char[strlen(src->prev_effects.values[i]) + 1]);
- strcpy(ptr, src->prev_effects.values[i]);
- }
+ for( int i=0; i < src->next_effects.total; ++i )
+ next_effects.append(cstrdup(src->next_effects[i]));
+ for( int i=0; i < src->prev_effects.total; ++i )
+ prev_effects.append(cstrdup(src->prev_effects[i]));
copy_params(src);
}
+int VFrame::copy_vframe(VFrame *frame)
+{
+ copy_stacks(frame);
+ return copy_from(frame);
+}
+
int VFrame::equal_stacks(VFrame *src)
{
for(int i = 0; i < src->next_effects.total && i < next_effects.total; i++)
return get_h() * get_bytes_per_line();
}
-void VFrame::draw_pixel(int x, int y)
+void VFrame::set_pixel_color(int rgb)
{
- if(!(x >= 0 && y >= 0 && x < get_w() && y < get_h())) return;
+ pixel_rgb = rgb;
+ int ir = 0xff & (pixel_rgb >> 16);
+ int ig = 0xff & (pixel_rgb >> 8);
+ int ib = 0xff & (pixel_rgb >> 0);
+ YUV::yuv.rgb_to_yuv_8(ir, ig, ib);
+ pixel_yuv = (ir<<16) | (ig<<8) | (ib<<0);
+}
-#define DRAW_PIXEL(x, y, components, do_yuv, max, type) \
-{ \
- type **rows = (type**)get_rows(); \
- rows[y][x * components] = max - rows[y][x * components]; \
- if(!do_yuv) \
- { \
- rows[y][x * components + 1] = max - rows[y][x * components + 1]; \
- rows[y][x * components + 2] = max - rows[y][x * components + 2]; \
- } \
- else \
- { \
- rows[y][x * components + 1] = (max / 2 + 1) - rows[y][x * components + 1]; \
- rows[y][x * components + 2] = (max / 2 + 1) - rows[y][x * components + 2]; \
- } \
- if(components == 4) \
- rows[y][x * components + 3] = max; \
+void VFrame::set_stiple(int mask)
+{
+ stipple = mask;
}
+int VFrame::draw_pixel(int x, int y)
+{
+ if( x < 0 || y < 0 || x >= get_w() || y >= get_h() ) return 1;
+ if( draw_point ) return (this->*draw_point)(x, y);
- switch(get_color_model())
- {
- case BC_RGB888:
- DRAW_PIXEL(x, y, 3, 0, 0xff, unsigned char);
- break;
- case BC_RGBA8888:
- DRAW_PIXEL(x, y, 4, 0, 0xff, unsigned char);
- break;
- case BC_RGB_FLOAT:
- DRAW_PIXEL(x, y, 3, 0, 1.0, float);
- break;
- case BC_RGBA_FLOAT:
- DRAW_PIXEL(x, y, 4, 0, 1.0, float);
- break;
- case BC_YUV888:
- DRAW_PIXEL(x, y, 3, 1, 0xff, unsigned char);
- break;
- case BC_YUVA8888:
- DRAW_PIXEL(x, y, 4, 1, 0xff, unsigned char);
- break;
- case BC_RGB161616:
- DRAW_PIXEL(x, y, 3, 0, 0xffff, uint16_t);
- break;
- case BC_YUV161616:
- DRAW_PIXEL(x, y, 3, 1, 0xffff, uint16_t);
- break;
- case BC_RGBA16161616:
- DRAW_PIXEL(x, y, 4, 0, 0xffff, uint16_t);
- break;
- case BC_YUVA16161616:
- DRAW_PIXEL(x, y, 4, 1, 0xffff, uint16_t);
- break;
+#define DRAW_PIXEL(type, r, g, b) { \
+ type **rows = (type**)get_rows(); \
+ rows[y][x * components + 0] = r; \
+ rows[y][x * components + 1] = g; \
+ rows[y][x * components + 2] = b; \
+ if( components == 4 ) \
+ rows[y][x * components + 3] = mx; \
+}
+ int components = BC_CModels::components(color_model);
+ int bch = BC_CModels::calculate_pixelsize(color_model) / components;
+ int sz = 8*bch, mx = BC_CModels::is_float(color_model) ? 1 : (1<<sz)-1;
+ int is_yuv = BC_CModels::is_yuv(color_model);
+ int pixel_color = is_yuv ? pixel_yuv : pixel_rgb;
+ int ir = 0xff & (pixel_color >> 16); float fr = 0;
+ int ig = 0xff & (pixel_color >> 8); float fg = 0;
+ int ib = 0xff & (pixel_color >> 0); float fb = 0;
+ if( (x+y) & stipple ) {
+ ir = 255 - ir; ig = 255 - ig; ib = 255 - ib;
+ }
+ if( BC_CModels::is_float(color_model) ) {
+ fr = ir / 255.; fg = ig / 255.; fb = ib / 255.;
+ mx = 1;
+ }
+ else if( (sz-=8) > 0 ) {
+ ir <<= sz; ig <<= sz; ib <<= sz;
+ }
+
+ switch(get_color_model()) {
+ case BC_RGB888:
+ case BC_YUV888:
+ case BC_RGBA8888:
+ case BC_YUVA8888:
+ DRAW_PIXEL(uint8_t, ir, ig, ib);
+ break;
+ case BC_RGB161616:
+ case BC_YUV161616:
+ case BC_RGBA16161616:
+ case BC_YUVA16161616:
+ DRAW_PIXEL(uint16_t, ir, ig, ib);
+ break;
+ case BC_RGB_FLOAT:
+ case BC_RGBA_FLOAT:
+ DRAW_PIXEL(float, fr, fg, fb);
+ break;
}
+ return 0;
}
}
}
+
void VFrame::draw_rect(int x1, int y1, int x2, int y2)
{
draw_line(x1, y1, x2, y1);
draw_line(x1, y2 - 1, x1, y1 + 1);
}
+
+void VFrame::draw_oval(int x1, int y1, int x2, int y2)
+{
+ int w = x2 - x1;
+ int h = y2 - y1;
+ int center_x = (x2 + x1) / 2;
+ int center_y = (y2 + y1) / 2;
+ int x_table[h / 2];
+
+//printf("VFrame::draw_oval %d %d %d %d %d\n", __LINE__, x1, y1, x2, y2);
+
+ for(int i = 0; i < h / 2; i++) {
+// A^2 = -(B^2) + C^2
+ x_table[i] = (int)(sqrt(-SQR(h / 2 - i) + SQR(h / 2)) * w / h);
+//printf("VFrame::draw_oval %d i=%d x=%d\n", __LINE__, i, x_table[i]);
+ }
+
+ for(int i = 0; i < h / 2 - 1; i++) {
+ int x3 = x_table[i];
+ int x4 = x_table[i + 1];
+
+ if(x4 > x3 + 1) {
+ for(int j = x3; j < x4; j++) {
+ draw_pixel(center_x + j, y1 + i);
+ draw_pixel(center_x - j, y1 + i);
+ draw_pixel(center_x + j, y2 - i - 1);
+ draw_pixel(center_x - j, y2 - i - 1);
+ }
+ }
+ else {
+ draw_pixel(center_x + x3, y1 + i);
+ draw_pixel(center_x - x3, y1 + i);
+ draw_pixel(center_x + x3, y2 - i - 1);
+ draw_pixel(center_x - x3, y2 - i - 1);
+ }
+ }
+
+ draw_pixel(center_x, y1);
+ draw_pixel(center_x, y2 - 1);
+ draw_pixel(x1, center_y);
+ draw_pixel(x2 - 1, center_y);
+ draw_pixel(x1, center_y - 1);
+ draw_pixel(x2 - 1, center_y - 1);
+}
+
+
void VFrame::draw_arrow(int x1, int y1, int x2, int y2, int sz)
{
double angle = atan((float)(y2 - y1) / (float)(x2 - x1));