/*************************************************************** ** ** Broadcom Corp. Confidential ** Copyright 2002 Broadcom Corp. All Rights Reserved. ** ** THIS SOFTWARE MAY ONLY BE USED SUBJECT TO AN EXECUTED ** SOFTWARE LICENSE AGREEMENT BETWEEN THE USER AND BROADCOM. ** YOU HAVE NO RIGHT TO USE OR EXPLOIT THIS MATERIAL EXCEPT ** SUBJECT TO THE TERMS OF SUCH AN AGREEMENT. ** ** Description: bgfx core implementation ** ** Created: 8/22/2002 by Jeffrey P. Fisher ** ** ** ****************************************************************/ /** include files **/ #include "bgfx.h" #define ZORDER_TUNNEL 3 #define ZORDER_FX 2 #define ZORDER_TOP 1 #define ZORDER_BOTTOM 0 #define SURF_WIDTH 720 #define BGFX_MIN(x,y) ((x < y) ? x : y) #define bgfx_clut8_to_rgb(clut,c) (uint32_t)(clut[c]) #define MAX_TEXT_BOX_CHARS 512 #define MAX_LINES 16 static unsigned long s_null_char = 0x00000000; static unsigned long s_space_char = 0x00000020; static unsigned long s_hyphen_char = 0x0000002D; static unsigned long s_newline_char = 0x0000000A; /* structure to maintain per-line info */ typedef struct lineinfo_t { uint16_t w,h; /* width and height of line */ uint16_t e; /* where line ends in text string */ uint16_t c; /* number of printable chars on line */ } lineinfo_t; /* blit row a1 to clut 8 destination */ static inline void bgfx_render_a1_to_clut4( uint8_t *src, uint8_t *dst, int width, uint8_t c, int odd_pixel ) { int i; BGFX_TRACE(("%p,%p,%d,0x%02x\n",src,dst,width,c)); for (i = 0; i < width; i++) { if (src[i>>3] & (1<<(7-(i%8)))) { if (odd_pixel & 1) { *dst &= 0xF0; *dst |= (c & 0xF); } else { *dst &= 0xF; *dst |= (c & 0xF) << 4; } BGFX_TRACE(("%d,",i)); } if (odd_pixel) { odd_pixel = 0; dst++; } else odd_pixel = 1; } BGFX_TRACE(("\n")); } /* blit row a8 to clut 8 destination */ static inline void bgfx_render_a8_to_clut4( uint8_t *src, uint8_t *dst, int width, uint8_t c ) { int i; for (i = 0; i < width; i++) { if (*src > 0x80) { if (i & 1) { *dst &= 0xF0; *dst |= (c & 0xF); } else { *dst &= 0xF; *dst |= (c & 0xF) << 4; } } BGFX_TRACE(("0x%02x(0x%02x)",*src,*dst)); if (i & 1) dst++; src++; } BGFX_TRACE(("\n")); } /* blit row a1 to clut 8 destination */ static inline void bgfx_render_a1_to_clut8( uint8_t *src, uint8_t *dst, int width, uint8_t c ) { int i; BGFX_TRACE(("%p,%p,%d,0x%02x\n",src,dst,width,c)); for (i = 0; i < width; i++) { if (src[i>>3] & (1<<(7-(i%8)))) { *dst = c; BGFX_TRACE(("%d,",i)); } dst++; } BGFX_TRACE(("\n")); } /* blit row a8 to clut 8 destination */ static inline void bgfx_render_a8_to_clut8( uint8_t *src, uint8_t *dst, int width, uint8_t c ) { int i; for (i = 0; i < width; i++) { if (*src > 0x80) { *dst = c; } BGFX_TRACE(("0x%02x(0x%02x)",*src,*dst)); dst++; src++; } BGFX_TRACE(("\n")); } /* blit row a1 to 8888 ARGB destination */ static inline void bgfx_render_a1_to_rgb( uint8_t *src, uint8_t *dst, int width, uint32_t c ) { int i; BGFX_TRACE(("%p,%p,%d,0x%08x\n",src,dst,width,c)); for (i = 0; i < width; i++) { if (src[i>>3] & (1<<(7-(i%8)))) { *((uint32_t*)dst) = c; BGFX_TRACE(("%d,",i)); } dst += 4; } BGFX_TRACE(("\n")); } /* blit row a8 to 8888 ARGB destination */ /* #define BGFX_BLEND(a,rs,rd) (((rs * a)/0xFF) + (rd - ((a * rd)/0xFF)) & 0xFF) */ #define BGFX_BLEND(a,rs,rd) ((a * (rs-rd) >> 8) + rd) static inline void bgfx_render_a8_to_rgb( uint8_t *src, uint8_t *dst, int width, uint32_t c) { register int i; register uint32_t a,r,g,b; register uint32_t a_src; register uint32_t r_src = (c >> 16) & 0xFF; register uint32_t g_src = (c >> 8) & 0xFF; register uint32_t b_src = (c >> 0) & 0xFF; for (i = 0; i < width; i++) { a_src = *src; a = dst[3]; r = dst[2]; g = dst[1]; b = dst[0]; *((uint32_t*)dst) = ((a_src==0)?(a<<24):(c&0xFF000000)) | (BGFX_BLEND(a_src,r_src,r) << 16) | (BGFX_BLEND(a_src,g_src,g) << 8) | BGFX_BLEND(a_src,b_src,b); BGFX_TRACE(("0x%02x(0x%08x)",*src,*((uint32_t*)dst))); dst += 4; src++; } BGFX_TRACE(("\n")); } /****************************************************************} * INPUTS: io and protection function structures * OUTPUTS: none * RETURNS: non-zero on failure * FUNCTION: initialize sgl, primarily for handling freetype * ****************************************************************/ int bgfx_init(bgfx_io_t *io_p,bgfx_prot_t *prot_p,bgfx_hw_t *hw_p) { bgfx_config(io_p,prot_p,hw_p); return bgfx_font_init(); } /****************************************************************} * INPUTS: none * OUTPUTS: none * RETURNS: non-zero on failure * FUNCTION: release sgl global resources, primarily for handling freetype * ****************************************************************/ void bgfx_done(void) { bgfx_font_done(); } /****************************************************************} * bgfx_create * * INPUTS: node_name - name of the node to create surface in * p - surface structure to initialize * OUTPUTS: none * RETURNS: non-zero on failure * FUNCTION: initialize and create the surface. * ****************************************************************/ int bgfx_create(bgfx_surf_p p,uint16_t width,uint16_t height, uint8_t *ptr, uint16_t pitch, bgfx_palette_t *palette, uint32_t flags) { void *tmp_surf = NULL; BGFX_DEBUG(("bgfx_create flags = 0x%08x\n",flags)); if ((flags & BGFX_SURF_GRC) && bgfx_hw_get_fcn() && p->surface.surf) { tmp_surf = p->surface.surf; } BGFX_MEMSET(p,0,sizeof(bgfx_surf_t)); if (!ptr && (flags & BGFX_SURF_PRIMARY)) { BGFX_DEBUG(("bgfx_create BGFX_SURF_FULLSCREEN requires valid memory ptr.\n")); } p->bpp = BGFX_SURF_GET_BPP(flags); BGFX_DEBUG(("bgfx_create bpp = %d.\n",p->bpp)); if ((p->bpp == 0) || ((flags & BGFX_SURF_RGB) && (p->bpp != 32))) { BGFX_DEBUG(("bgfx_create invalid bpp = %d.\n",p->bpp)); return -1; } if ((flags & BGFX_SURF_GRC) && !bgfx_hw_get_fcn()) { flags &= ~BGFX_SURF_GRC; } p->flags = flags; if (ptr) { p->surface.pitch = pitch; p->surface.buf = ptr; p->surface.width = width; p->surface.height = height; if (p->flags & BGFX_SURF_GRC) { p->surface.surf = tmp_surf; } else { p->surface.surf = NULL; } } else { p->surface.pitch = width * p->bpp / 8; if (p->surface.pitch & 0x1) p->surface.pitch += 1; p->surface.width = width; p->surface.height = height; p->surface.surf = NULL; if (p->flags & BGFX_SURF_GRC) { int format = (flags & BGFX_SURF_RGB) ? 5 /*bgraphics_pixel_format_a8_r8_g8_b8*/ : 10/*bgraphics_pixel_format_y08_cb8_y18_cr8*/; uint32_t pitch; if (bgfx_hw_surf_create(width, height,format,&(p->surface.buf),&pitch, (void*)&(p->surface.surf)) != BERR_SUCCESS) { return -1; } p->flags |= BGFX_SURF_ALLOC; } else { p->surface.buf = BGFX_MALLOC(p->surface.pitch * p->surface.height); p->flags |= BGFX_SURF_ALLOC; } } if (palette) { BGFX_MEMCPY(&p->palette,palette,sizeof(bgfx_palette_t)); } return 0; } /**************************************************************** * bgfx_destroy * * INPUTS: p - surface structure * OUTPUTS: none * RETURNS: none * FUNCTION: free up surface resources * ****************************************************************/ void bgfx_destroy(bgfx_surf_p p) { if ((p->flags & BGFX_SURF_GRC) && p->surface.surf && (p->flags & BGFX_SURF_ALLOC) ) { bgfx_hw_surf_destroy(p->surface.surf); } if (p->flags & BGFX_SURF_ALLOC) { BGFX_FREE(p->surface.buf); return; } } /**************************************************************** * bgfx_fill_rect * * INPUTS: p - surface structure * OUTPUTS: w,h,flags - surface width,height and flags * RETURNS: none * FUNCTION: Return surface information (width,height, and flags) * ****************************************************************/ void bgfx_get_info(bgfx_surf_p p,uint16_t *w,uint16_t *h,uint16_t *pitch, uint32_t *flags) { *w = p->surface.width; *h = p->surface.height; *pitch = p->surface.pitch; *flags = p->flags; } /**************************************************************** * bgfx_get_palette * * INPUTS: p - surface structure * OUTPUTS: palette_p - return current palette * RETURNS: none * FUNCTION: get the current palett * ****************************************************************/ void bgfx_get_palette(bgfx_surf_p p,bgfx_palette_t *palette_p) { BGFX_MEMCPY(palette_p,&p->palette,sizeof(bgfx_palette_t)); } /**************************************************************** * bgfx_set_palette * * INPUTS: p - surface structure * palette_p - the palette * OUTPUTS: none * RETURNS: none * FUNCTION: set the global system palette * ****************************************************************/ void bgfx_set_palette(bgfx_surf_p p, bgfx_palette_t *palette_p) { BGFX_MEMCPY(&p->palette,palette_p,sizeof(bgfx_palette_t)); } /**************************************************************** * bgfx_set_pixel * * INPUTS: p - surface structure * x,y - coordinates * c - pixel value * OUTPUTS: none * RETURNS: none * FUNCTION: set the pixel at x,y * ****************************************************************/ void bgfx_set_pixel(bgfx_surf_p p,uint16_t x,uint16_t y,bgfx_pixel c) { uint8_t *dst; if ((x >= p->surface.width) || (y >= p->surface.height)) { BGFX_DEBUG(("bgfx_set_pixel param error (%p,%d,%d)\n",p,x,y)); return; } dst = (uint8_t*)p->surface.buf; dst += y * p->surface.pitch + (x * p->bpp)/8; if (p->flags & BGFX_SURF_RGB) { *((uint32_t*)dst) = c; bgfx_cacheflush(dst,4); } else if (p->bpp == 8) { *dst = (uint8_t)c; bgfx_cacheflush(dst,1); } else { if (x & 1) { *dst &= 0xF0; *dst |= (uint8_t)(c & 0xF); } else { *dst &= 0xF; *dst |= (uint8_t)((c & 0xF) << 4); } bgfx_cacheflush(dst,1); } } /**************************************************************** * bgfx_get_pixel * * INPUTS: p - surface structure * x,y - coordinates * c - pixel value * OUTPUTS: none * RETURNS: none * FUNCTION: get the pixel value at x,y. The bgfx_color will be the * actual pixel value. * * NOTE: Not supported for RGB surfaces. To get a pixel for RGB access buf * directly. * ****************************************************************/ bgfx_pixel bgfx_get_pixel(bgfx_surf_p p,uint16_t x,uint16_t y) { uint8_t *dst; if ((x >= p->surface.width) || (y >= p->surface.height)) { BGFX_DEBUG(("bgfx_get_pixel param error (%p,%d,%d)\n",p,x,y)); return 0; } dst = (uint8_t*)p->surface.buf; dst += y * p->surface.pitch + (x * p->bpp)/8; if (p->flags & BGFX_SURF_RGB) { return *((uint32_t*)dst); } else if (p->bpp == 8) { return *dst; } /* p->bpp == 4 */ if (x & 1) { return (*dst & 0xF0); } return (*dst >> 4); } /**************************************************************** * bgfx_h_draw_line * * INPUTS: p - surface structure * x1,x2,y - coordinates * c - pixel value * OUTPUTS: none * RETURNS: none * FUNCTION: Draw a horizontal line make of pixels defined by the value c. * * JPF - these need to be optimized * ****************************************************************/ void bgfx_h_draw_line(bgfx_surf_p p,uint16_t x1,uint16_t x2,uint16_t y, bgfx_pixel c) { int w = x2 - x1; if (w < 0) { w = -w; bgfx_fill_rect(p,x2,y,(uint16_t)w,1, c); } else { bgfx_fill_rect(p,x1,y,(uint16_t)w,1, c); } } /**************************************************************** * bgfx_v_draw_line * * INPUTS: p - surface structure * x,y1,y2 - coordinates * c - pixel value * OUTPUTS: none * RETURNS: none * FUNCTION: Draw a vertical line make of pixels defined by the value c. * * JPF - these need to be optimized * ****************************************************************/ void bgfx_v_draw_line(bgfx_surf_p p,uint16_t x,uint16_t y1,uint16_t y2, bgfx_pixel c) { uint8_t *dst; uint16_t h = y2 - y1; if ((y2 <= y1) || (x >= p->surface.width)) { BGFX_DEBUG(("bgfx_v_draw_line param error (%p,%d,%d,%d,0x%08x)\n",p,x,y1,y2,c)); return; } if (h > p->surface.height - y1) h = p->surface.height - y1; dst = (uint8_t*)p->surface.buf; dst += y1 * p->surface.pitch + x * p->bpp/8; if (p->flags & BGFX_SURF_RGB) { while (h--) { *((uint32_t*)dst) = c; dst += p->surface.pitch; } } else if (p->bpp == 8) { while (h--) { *dst = (uint8_t)c; dst += p->surface.pitch; } } else { while (h--) { if (x & 1) { *dst &= 0xF0; *dst |= (uint8_t)(c & 0xF); } else { *dst &= 0xF; *dst |= (uint8_t)((c & 0xF) << 4); } dst += p->surface.pitch; } } bgfx_cacheflush(p->surface.buf,p->surface.pitch * p->surface.height); } /**************************************************************** * bgfx_fill_rect * * INPUTS: p - surface structure * x1,x2,y1,y2 - coordinates * c - pixel value * OUTPUTS: none * RETURNS: none * FUNCTION: Fill the rectangle with pixels defined by the value c. * * JPF - these need to be optimized * ****************************************************************/ /* TODO create optimized version of memset */ #if 0 static inline void *bgfx_memset(void *dest,int c,size_t count) { uint8_t *d = dest; for(;count>0;count--) { *d++ = c; } return dest; } #else #define bgfx_memset BGFX_MEMSET #endif void bgfx_fill_rect(bgfx_surf_p p,uint16_t x,uint16_t y,uint16_t w,uint16_t h, bgfx_pixel c) { uint8_t *tdst; uint16_t i; uint8_t *dst; uint16_t j; if ((x > p->surface.width) || (y > p->surface.height)) { BGFX_DEBUG(("bgfx_fill_rect param error (%p,%d,%d,%d,%d,0x%08x)\n",p,x,y,w,h,c)); return; } if (y + h > p->surface.height) h = p->surface.height - y; if (w + x > p->surface.width) w = p->surface.width - x; if (p->flags & BGFX_SURF_GRC) { if (!w || !h) return; bgfx_rect rect; rect.x = x; rect.y = y; rect.width = w; rect.height = h; bgfx_hw_fill((void *)p->surface.surf, &rect, c); return ; } dst = (uint8_t*)p->surface.buf; dst += y * p->surface.pitch + x * p->bpp/8; if (p->flags & BGFX_SURF_RGB) { for (j = 0; j < h; ++j) { tdst = dst; for (i = 0; i < w; ++i) { *((uint32_t*)tdst) = c; tdst += p->bpp/8; } dst += p->surface.pitch; } } else if (p->bpp == 8) { for (j = 0; j < h; ++j) { bgfx_memset(dst,(uint8_t)c,w); dst += p->surface.pitch; } } else { unsigned char two_pixel = (uint8_t)((c << 4) | c); for (j = 0; j < h; ++j) { unsigned int num_bytes = (w * p->bpp)/8; if (x & 1) { *dst &= 0xF0; *dst |= (uint8_t)(c & 0xF); if (w & 1) { bgfx_memset(&dst[1],two_pixel,num_bytes); } else { bgfx_memset(&dst[1],two_pixel,num_bytes - 1); dst[num_bytes] &= 0x0F; dst[num_bytes] |= (uint8_t)((c & 0xF) << 4); } } else { if (w & 1) { bgfx_memset(dst,two_pixel,num_bytes); dst[num_bytes] &= 0x0F; dst[num_bytes] |= (uint8_t)((c & 0xF) << 4); } else bgfx_memset(dst,two_pixel,num_bytes); } dst += p->surface.pitch; } } bgfx_cacheflush(p->surface.buf,p->surface.pitch * p->surface.height); } /**************************************************************** * bgfx_render_glyph * * INPUTS: p - surface structure * x,y - coordinates * a_char - character to render * font_p - font definition * c - color * OUTPUTS: none * RETURNS: character advance * FUNCTION: Fill the rectangle with pixels defined by the value c. * ****************************************************************/ #define IT_OFFSET 3 static inline int bgfx_render_glyph(bgfx_surf_p p,uint16_t x,uint16_t y, const unsigned long a_char, bgfx_font_t *font_p,bgfx_pixel c, bgfx_glyph_t * p_bgfx_glyph,bgfx_text_style style) { int max_width,ybase; uint16_t row; uint8_t *src,*dst; bgfx_glyph_t * bgfx_glyph = p_bgfx_glyph; unsigned char glyph_width; /* width of the rendered character bitmap in pixels */ unsigned char glyph_height; /* height of the rendered character bitmap in pixels */ ybase = y; if (x > p->surface.width) { BGFX_TRACE(("bgfx_render_glyph x > width (%d,%d)\n",x,p->surface.width)); return 0; } /* if using glyph cache use the get glyph rather than render directly to surface buffer */ if (!bgfx_glyph && (font_p->cache_size > 0)) { bgfx_glyph = bgfx_get_glyph(font_p,a_char); } if (!bgfx_glyph) { BGFX_DEBUG(("bgfx_render_glyph could not load glyph 0x%08lx\n",a_char)); return 0; } BGFX_TRACE(("bgfx_render_glyph glyph (%p,%d,%d)\n",bgfx_glyph->buf,bgfx_glyph->height,bgfx_glyph->width)); glyph_width = bgfx_glyph->width; glyph_height = bgfx_glyph->height; if (bgfx_glyph->left + x > 0) x += bgfx_glyph->left; if (y-bgfx_glyph->top > 0) y -= bgfx_glyph->top; else y = 0; if (glyph_height + y > p->surface.height) glyph_height = p->surface.height - y; if (glyph_width + x > p->surface.width) glyph_width = p->surface.width - x; src = (uint8_t*)bgfx_glyph->buf; dst = (uint8_t*)p->surface.buf; BGFX_TRACE(("bgfx_render_glyph suface (%p(y = %d,x = %d,pitch = %d,bpp = %d)\n",dst,y,x,p->surface.pitch, p->bpp)); dst += y * p->surface.pitch + x * p->bpp/8; if (style & eBGFX_ITALIC) max_width = BGFX_MIN(glyph_width + IT_OFFSET, p->surface.width - x); else max_width = BGFX_MIN(glyph_width, p->surface.width - x); if (max_width <= 0) { if (glyph_width != 0) { BGFX_TRACE(("bgfx_render_glyph glyph_width = %d\n", glyph_width)); } return bgfx_glyph->advance; } /* Render mono glyph */ if (font_p->format & BGFX_MONO) { BGFX_TRACE(("bgfx_render_glyph Alpha 1 font\n")); if (p->flags & BGFX_SURF_RGB) { uint16_t adjx = 0; for (row = 0; row < glyph_height; row++) { if (style & eBGFX_ITALIC) { adjx = IT_OFFSET - ((row*IT_OFFSET)/glyph_height); } bgfx_render_a1_to_rgb( src, dst+adjx*p->bpp/8, max_width,c ); src += bgfx_glyph->pitch; dst += p->surface.pitch; } } else if (p->bpp == 8) { for (row = 0; row < glyph_height; row++) { bgfx_render_a1_to_clut8( src, dst, max_width,c ); src += bgfx_glyph->pitch; dst += p->surface.pitch; } } else { uint16_t adjx = x; for (row = 0; row < glyph_height; row++) { if (style & eBGFX_ITALIC) { adjx = x + IT_OFFSET - ((row * IT_OFFSET)/glyph_height); dst = (uint8_t*)p->surface.buf; dst += (y + row) * p->surface.pitch + adjx * p->bpp/8; } bgfx_render_a1_to_clut4( src, dst, max_width,c, adjx & 0x1 ); src += bgfx_glyph->pitch; dst += p->surface.pitch; } } } else /* Render 8 bit grayscale glyph */ { BGFX_TRACE(("bgfx_render_glyph Alpha 8 font\n")); if (p->flags & BGFX_SURF_RGB) { uint16_t adjx = 0; for (row = 0; row < glyph_height; row++) { if (style & eBGFX_ITALIC) { adjx = IT_OFFSET - ((row*IT_OFFSET)/glyph_height); } /* TODO:: it's for nanotv up/down/left/right cursor, which is from mono type font */ if (a_char >= 0x193 && a_char <= 0x196) bgfx_render_a1_to_rgb(src, dst+adjx*p->bpp/8, glyph_width,c); else bgfx_render_a8_to_rgb( src, dst+adjx*p->bpp/8, glyph_width,c); src += bgfx_glyph->pitch; dst += p->surface.pitch; } } else if (p->bpp == 8) { for (row = 0; row < glyph_height; row++) { bgfx_render_a8_to_clut8( src, dst, glyph_width,c ); src += bgfx_glyph->pitch; dst += p->surface.pitch; } } else { for (row = 0; row < glyph_height; row++) { bgfx_render_a8_to_clut4( src, dst, glyph_width,c ); src += bgfx_glyph->pitch; dst += p->surface.pitch; } } } if (style & eBGFX_UNDERLINE) { bgfx_h_draw_line(p,x,x + bgfx_glyph->advance,ybase,c); bgfx_h_draw_line(p,x,x + bgfx_glyph->advance,ybase+1,c); } return bgfx_glyph->advance; } /**************************************************************** * bgfx_draw_text * * INPUTS: p - surface structure * x,y - coordinates * str_p - array of 32-bit unicode characters str_len - number of 32-bit characters in array * font_p - font definition * c - color * OUTPUTS: none * RETURNS: none * FUNCTION: Fill the rectangle with pixels defined by the value c. * ****************************************************************/ void bgfx_draw_text(bgfx_surf_p p,uint16_t x,uint16_t y, const unsigned long *str_p, int str_len, bgfx_font_t *font_p,bgfx_pixel c, bgfx_text_style style) { int offset; BGFX_TRACE(("bgfx_draw_text %d characters.\n",str_len)); for (offset = 0; offset < str_len; offset++) { x += bgfx_render_glyph(p,x,y, str_p[offset], font_p,c,NULL,style); } bgfx_cacheflush(p->surface.buf,p->surface.pitch * p->surface.height); } /**************************************************************** * bgfx_draw_text_box * * INPUTS: p - surface structure * x,y - coordinates * width,height - width and hegith of the text box to draw * str_p - array of 32-bit unicode characters str_len - number of 32-bit characters in array * font_p - font definition * c - color line_spacing - line space * OUTPUTS: none * RETURNS: none * FUNCTION: Fill the rectangle with pixels defined by the value c. * ****************************************************************/ //#define MAX_TEXT_BOX_CHARS 512 //#define MAX_LINES 16 //static unsigned long s_test_char = 0x00000057UL; /* TODO: Might want to improve macro to check for chars other than spaces */ #define BGFX_IS_BREAK(x) ((x == 0x00000020) ? 1 : 0) void bgfx_draw_text_box(bgfx_surf_p p,uint16_t x,uint16_t y, uint16_t width, uint16_t height, const unsigned long *str_p, int str_len, bgfx_font_t *font_p,bgfx_pixel c, uint16_t line_spacing) { int offset,bw_off,line_idx; static bgfx_glyph_t * p_bgfx_glyph[MAX_TEXT_BOX_CHARS]; /* Not thread safe */ static uint8_t brk_loc[MAX_TEXT_BOX_CHARS]; uint16_t max_h[MAX_LINES],ch_w,test_max_h,bot; BGFX_TRACE(("bgfx_draw_text_box(%d,%d,%d,%d,%d) %d characters.\n",x,y,width,height,line_spacing,str_len)); if (str_len > MAX_TEXT_BOX_CHARS) { BGFX_DEBUG(("bgfx_draw_text %d > %d maximum characters.\n",str_len,MAX_TEXT_BOX_CHARS)); str_len = MAX_TEXT_BOX_CHARS; } y += font_p->ascender; bot = y + height; test_max_h = font_p->height; for (line_idx = 0; line_idx < MAX_LINES; ++line_idx) max_h[line_idx] = test_max_h; ch_w = 0; line_idx = 0; for (offset = 0; offset < str_len; offset++) { brk_loc[offset] = 0; p_bgfx_glyph[offset] = bgfx_get_glyph(font_p,str_p[offset]); if (p_bgfx_glyph[offset] == NULL) { BGFX_DEBUG(("%s:%d str[%d] = 0x%08lx\n",__FUNCTION__,__LINE__,offset,str_p[offset])); /* if not a printable charactor, then use space instead */ p_bgfx_glyph[offset] = bgfx_get_glyph(font_p,0x00000020); continue; } ch_w += p_bgfx_glyph[offset]->advance; if (p_bgfx_glyph[offset]->height > max_h[line_idx]) max_h[line_idx] = p_bgfx_glyph[offset]->height; if (ch_w >= width) { line_idx++; for (bw_off = offset - 1; bw_off >= 0; --bw_off) { if (brk_loc[bw_off]) { bw_off = -1; break; } if (BGFX_IS_BREAK(str_p[bw_off])) { brk_loc[bw_off] = 1; break; } } /* last resort, break on preceding character */ if (bw_off < 0) { brk_loc[offset - 1] = 1; ch_w = p_bgfx_glyph[offset]->advance; } else { offset = bw_off; ch_w = 0; } } } /* offset y so text is inside box */ line_idx = 0; ch_w = 0; for (offset = 0; (offset < str_len) && (line_idx < MAX_LINES); offset++) { if (brk_loc[offset]) { line_idx++; ch_w = 0; #ifdef BGFX_VARIABL_LINE_HEIGHT y += line_spacing + max_h[line_idx]; if ((y - max_h[line_idx]) > bot) break; #else y += line_spacing + test_max_h; if ((y - test_max_h) > bot) break; #endif continue; } ch_w += bgfx_render_glyph(p,x + ch_w,y, str_p[offset], font_p,c,p_bgfx_glyph[offset],eBGFX_NORMAL); } bgfx_cacheflush(p->surface.buf,p->surface.pitch * p->surface.height); } static inline void bgfx_csc_YCbCr_to_RGB(uint32_t *ycbcr) { uint8_t y,cb,cr,r,g,b; y = *ycbcr>>16; cb = *ycbcr>>8; cr = *ycbcr; r = 1.164*(y-16)+1.596*(cr-128); g = 1.164*(y-16)-0.813*(cr-128)-0.391*(cb-128); b = 1.164*(y-16)+2.018*(cb-128); *ycbcr = (*ycbcr&0xFF000000)|(r<<16)|(g<<8)|b; } /**************************************************************** * INPUTS: s_pixel,d_pixel - source and destination pixles * OUTPUTS: none * RETURNS: blended pixel * FUNCTION: blend argb pixels * ****************************************************************/ static inline uint32_t bgfx_blend_argb_pixels(uint32_t s_pixel,uint32_t d_pixel) { uint32_t sr,sg,sb,sa,dr,dg,db,da,oa; sa = (s_pixel >> 24) & 0xFF; da = (d_pixel >> 24) & 0xFF; if ((sa == 0xFF) || (da == 0x00)) { return s_pixel; } if ((da == 0xFF) && (sa == 0x00)) { return d_pixel; } sr = (s_pixel >> 16) & 0xFF; sg = (s_pixel >> 8) & 0xFF; sb = (s_pixel >> 0) & 0xFF; dr = (d_pixel >> 16) & 0xFF; dg = (d_pixel >> 8) & 0xFF; db = (d_pixel >> 0) & 0xFF; dr = (((sr * sa)/0xFF) & 0xFF) + ((((0xFF - sa) * dr)/0xFF) & 0xFF); dg = (((sg * sa)/0xFF) & 0xFF) + ((((0xFF - sa) * dg)/0xFF) & 0xFF); db = (((sb * sa)/0xFF) & 0xFF) + ((((0xFF - sa) * db)/0xFF) & 0xFF); oa = da; return (oa << 24) | (dr << 16) | (dg << 8) | db; } /**************************************************************** * bgfx_render_file * * INPUTS: p - surface structure * x,y - coordinates * file_p - file to get image from * set_palette - use the palette in the file to set the * global palette. * OUTPUTS: none * RETURNS: non-zero on error * FUNCTION: Render the image in the file to the x,y location * ****************************************************************/ int bgfx_render_file(bgfx_surf_p p,uint16_t x,uint16_t y, void *file_p,int set_palette) { bcm_raw_8_t header; int result = -1; uint16_t row,rows,rowbytes,width; uint8_t *dst_ptr; int palette_bytes; uint16_t i; static uint32_t tbuf_32[SURF_WIDTH]; static uint8_t *tbuf = (uint8_t*)tbuf_32; if (bgfx_read(&header,1,sizeof(header),file_p) == sizeof(header)) { if (header.type != RAW_TYPE || header.version != RAW_VERSION) { BGFX_DEBUG(("bgfx_render_file failed version,type invalid(0x%08x,0x%08x)\n",header.type,header.version)); return -1; } BGFX_TRACE(("header.version = 0x%08x\n",header.version)); BGFX_TRACE(("header.type = 0x%08x\n",header.type)); BGFX_TRACE(("header.clut_size = %d\n",header.clut_size)); BGFX_TRACE(("header.color_key = 0x%02x\n",header.color_key)); BGFX_TRACE(("header.pitch = %d\n",header.pitch)); BGFX_TRACE(("header.width = 0x%08x\n",header.width)); BGFX_TRACE(("header.height = 0x%08x\n",header.height)); width = BGFX_MIN(header.width, (p->surface.width - x)); if (header.clut_size == 0) { if (!(p->flags & BGFX_SURF_RGB)) { BGFX_DEBUG(("bgfx_render_file failed, rendering ARGB to ARGB only\n")); return -1; } rows = BGFX_MIN(header.height, p->surface.height - y); rowbytes = header.pitch; dst_ptr = (uint8_t*)p->surface.buf; dst_ptr += y * p->surface.pitch + x * p->bpp/8; for (row = 0; row < rows; row++) { bgfx_read(tbuf ,1,rowbytes,file_p); for (i = 0; i < width; ++i) { ((uint32_t*)dst_ptr)[i] = bgfx_blend_argb_pixels(tbuf_32[i],((uint32_t*)dst_ptr)[i]); } dst_ptr += p->surface.pitch; } BGFX_TRACE(("Render - done\n")); bgfx_cacheflush(p->surface.buf,p->surface.pitch * p->surface.height); result = 0; return result; } palette_bytes = (sizeof(unsigned long) * header.clut_size); if (bgfx_read(&p->palette,1,palette_bytes,file_p) == palette_bytes) { if (set_palette && (header.clut_size == PALETTE_SIZE)) bgfx_set_palette(p,&p->palette); if (p->flags & BGFX_SURF_RGB) { for (i=0; ipalette.clut[i]); } } } if ((header.width == p->surface.width) && (header.height == p->surface.height) && (p->surface.pitch == header.pitch) && (x == 0) && (y == 0) && !(p->flags & BGFX_SURF_RGB)) { BGFX_TRACE(("Fast Render\n")); bgfx_read(p->surface.buf,1,header.pitch*header.height,file_p); } else if ((p->palette.clut[header.color_key] & 0xFF000000) != 0) { BGFX_TRACE(("Render - no color key\n")); // rowbytes = BGFX_MIN(header.width,p->surface.width - x) * p->bpp/8; rowbytes = header.pitch; rows = BGFX_MIN(header.height, p->surface.height - y); dst_ptr = (uint8_t*)p->surface.buf; dst_ptr += y * p->surface.pitch + x * p->bpp/8; if (p->flags & BGFX_SURF_RGB) { for (row = 0; row < rows; row++) { bgfx_read(tbuf ,1,rowbytes,file_p); for (i = 0; i < width; ++i) { uint32_t src_c,cnt=0; src_c = bgfx_clut8_to_rgb(p->palette.clut,(tbuf[i]>>4)&0x0f); half: cnt++; ((uint32_t*)dst_ptr)[i] = bgfx_blend_argb_pixels(src_c,((uint32_t*)dst_ptr)[i]); if (cnt == 2) continue; src_c = bgfx_clut8_to_rgb(p->palette.clut,(tbuf[i])&0x0f); goto half; } if (header.pitch > rowbytes) { bgfx_read(tbuf ,1,header.pitch - rowbytes,file_p); } dst_ptr += p->surface.pitch; } } else { for (row = 0; row < rows; row++) { bgfx_read(dst_ptr ,1,rowbytes,file_p); if (header.pitch > rowbytes) { bgfx_read(tbuf ,1,header.pitch - rowbytes,file_p); } dst_ptr += p->surface.pitch; } } } else { BGFX_TRACE(("Render - color key\n")); rowbytes = BGFX_MIN(header.width,(p->surface.width - x)) * p->bpp/8; rows = BGFX_MIN(header.height, (p->surface.height - y)); dst_ptr = (uint8_t*)p->surface.buf; dst_ptr += y * p->surface.pitch + x * p->bpp/8; if (p->flags & BGFX_SURF_RGB) { for (row = 0; row < rows; row++) { bgfx_read(tbuf ,1,rowbytes,file_p); for (i = 0; i < width; ++i) { if (tbuf[i] != header.color_key) { uint32_t src_c; src_c = bgfx_clut8_to_rgb(p->palette.clut,tbuf[i]); ((uint32_t*)dst_ptr)[i] = bgfx_blend_argb_pixels(src_c,((uint32_t*)dst_ptr)[i]); } } if (header.pitch > rowbytes) { bgfx_read(tbuf ,1,header.pitch - rowbytes,file_p); } dst_ptr += p->surface.pitch; } } else { for (row = 0; row < rows; row++) { bgfx_read(dst_ptr ,1,rowbytes,file_p); if (header.pitch > rowbytes) { bgfx_read(tbuf ,1,header.pitch - rowbytes,file_p); } dst_ptr += p->surface.pitch; } } } } BGFX_TRACE(("Render - done\n")); bgfx_cacheflush(p->surface.buf,p->surface.pitch * p->surface.height); result = 0; return result; } /**************************************************************** * bgfx_blit * * INPUTS: p_src,p_dst - surface structure * x,y - destination coordinates * OUTPUTS: none * RETURNS: non-zero on error * FUNCTION: Blit between the two surfaces, only the destination can be RGB * ****************************************************************/ static int32_t g_ai32_Matrix_RGBtoYCbCr_BT_709[20] = { (int32_t) ( 0.183f * (1 << 8)), /* R factor for Y */ (int32_t) ( 0.614f * (1 << 8)), /* G factor for Y */ (int32_t) ( 0.062f * (1 << 8)), /* B factor for Y */ (int32_t) 0, /* A factor for Y */ (int32_t) (16 * (1 << 8)), /* Increment for Y */ (int32_t) (-0.101f * (1 << 8)), /* R factor for Cb */ (int32_t) (-0.339f * (1 << 8)), /* G factor for Cb */ (int32_t) ( 0.439f * (1 << 8)), /* B factor for Cb */ (int32_t) 0, /* A factor for Cb */ (int32_t) (128 * (1 << 8)), /* Increment for Cb */ (int32_t) ( 0.439f * (1 << 8)), /* R factor for Cr */ (int32_t) (-0.339f * (1 << 8)), /* G factor for Cr */ (int32_t) (-0.040f * (1 << 8)), /* B factor for Cr */ (int32_t) 0, /* A factor for Cr */ (int32_t) (128 * (1 << 8)), /* Increment for Cr */ (int32_t) 0, /* R factor for A */ (int32_t) 0, /* G factor for A */ (int32_t) 0, /* B factor for A */ (int32_t) (1 << 8), /* A factor for A */ (int32_t) 0, /* Increment for A */ }; #define bgfx_csc_RGB_2_YCbCr_macro(y,cb,cr,R,G,B) \ { \ y =((m0*R + m1*G + m2*B + m4) >> shift); \ cb = ((m5*R + m6*G + m7*B + m9) >> shift); \ cr = ((m10*R + m11*G + m12*B + m14) >> shift); \ } int bgfx_blit(bgfx_surf_p p_src, bgfx_surf_p p_dst, uint16_t x,uint16_t y) { uint8_t *dst,*src; uint16_t j,w,h,rowbytes; uint16_t i; uint8_t *tdst; if ((p_src->flags & BGFX_SURF_GRC) && (p_dst->flags & BGFX_SURF_GRC)) { void *src,*dst,*out; bgfx_rect src_r,dst_r,out_r; src = p_src->surface.surf; out = p_dst->surface.surf; dst = NULL; src_r.x = dst_r.x = out_r.x = 0; src_r.y = dst_r.y = out_r.y = 0; src_r.width = p_src->surface.width; src_r.height = p_src->surface.height; dst_r.width = p_dst->surface.width; dst_r.height = p_dst->surface.height; out_r.width = p_dst->surface.width; out_r.height = p_dst->surface.height; bgfx_hw_blit(src,&src_r, dst, &dst_r,out, &out_r); return 0; } BGFX_TRACE(("bgfx_blit x,y (%d,%d)\n",x,y)); if ((x > p_dst->surface.width) || (y > p_dst->surface.height)) { BGFX_DEBUG(("bgfx_blit x,y (%d,%d) outside destination (%d,%d)\n",x,y, p_dst->surface.width,p_dst->surface.height)); return -1; } w = p_src->surface.width; if (w > p_dst->surface.width - x) w = p_dst->surface.width - x; h = p_src->surface.height; if (h > p_dst->surface.height - y) h = p_dst->surface.height - y; src = (uint8_t*)p_src->surface.buf; dst = (uint8_t*)p_dst->surface.buf; dst += y * p_dst->surface.pitch + x * p_dst->bpp/8; if (p_dst->flags & BGFX_SURF_RGB) { if (p_src->flags & BGFX_SURF_RGB) { uint16_t rows; rows = p_src->surface.height - y; rowbytes = p_src->surface.pitch; dst = (uint8_t*)p_dst->surface.buf; dst += y * p_dst->surface.pitch + x * p_dst->bpp/8; src = (uint8_t*)p_src->surface.buf; src += y * p_src->surface.pitch + x * p_src->bpp/8; for (j = 0; j < rows; j++) { for (i = 0; i < rowbytes/(p_src->bpp/8); ++i) { ((uint32_t*)dst)[i] = bgfx_blend_argb_pixels(((uint32_t*)src)[i],((uint32_t*)dst)[i]); } dst += p_dst->surface.pitch; src += p_src->surface.pitch; } } else { int32_t rgb, y1, cb, cr, a, r, g, b, shift; int32_t m0 = g_ai32_Matrix_RGBtoYCbCr_BT_709[0]; int32_t m1 = g_ai32_Matrix_RGBtoYCbCr_BT_709[1]; int32_t m2 = g_ai32_Matrix_RGBtoYCbCr_BT_709[2]; int32_t m4 = g_ai32_Matrix_RGBtoYCbCr_BT_709[4]; int32_t m5 = g_ai32_Matrix_RGBtoYCbCr_BT_709[5]; int32_t m6 = g_ai32_Matrix_RGBtoYCbCr_BT_709[6]; int32_t m7 = g_ai32_Matrix_RGBtoYCbCr_BT_709[7]; int32_t m9 = g_ai32_Matrix_RGBtoYCbCr_BT_709[9]; int32_t m10 = g_ai32_Matrix_RGBtoYCbCr_BT_709[10]; int32_t m11 = g_ai32_Matrix_RGBtoYCbCr_BT_709[11]; int32_t m12 = g_ai32_Matrix_RGBtoYCbCr_BT_709[12]; int32_t m14 = g_ai32_Matrix_RGBtoYCbCr_BT_709[14]; shift = 8; for (j = 0; j < h; ++j) { /* convert rgb to ycbcr */ tdst = dst; for (i=0; i<(w << 2); i+=4, tdst+=4) { rgb = *(uint32_t *)&src[i]; a = ((rgb>>24)&0xff); r = ((rgb>>16)&0xff); g = ((rgb>>8)&0xff); b = rgb&0xff; bgfx_csc_RGB_2_YCbCr_macro(y1,cb,cr,r,g,b); *((uint32_t *)tdst)= ((a<<24)|(y1<<16)|(cb<<8)|cr); } dst += p_dst->surface.pitch; src += p_src->surface.pitch; } } } else { rowbytes = BGFX_MIN(p_src->surface.width,p_dst->surface.width - x) * p_dst->bpp/8; for (j = 0; j < h; ++j) { BGFX_MEMCPY(dst,src,rowbytes); dst += p_dst->surface.pitch; src += p_src->surface.pitch; } } bgfx_cacheflush(p_dst->surface.buf,p_dst->surface.pitch * p_dst->surface.height); return 0; } /**************************************************************** * INPUTS: p_src,p_dst - source and destination buffer pointers * sx * dx * w - width * OUTPUTS: none * RETURNS: non-zero on error * FUNCTION: Blit a region of one surface to another (for 4 bpp ) * ****************************************************************/ static inline void bgfx_row_copy(unsigned char *src, unsigned char *dst, uint16_t sx,uint16_t dx,uint16_t w, int bpp) { if (bpp == 8) { BGFX_MEMCPY(dst,src,w); } else { unsigned int num_bytes = (w * 4)/8; int pix; if (sx & 1) { if (dx & 1) { *dst &= 0xF0; *dst |= (*src & 0xF); if (w & 1) { BGFX_MEMCPY(&dst[1],&src[1],num_bytes); } else { BGFX_MEMCPY(&dst[1],&src[1],num_bytes - 1); dst[num_bytes] &= 0x0F; dst[num_bytes] |= (src[num_bytes] & 0xF0); } } else { for (pix = 0; pix < w; ++pix) { if (pix & 1) { *dst &= 0xF0; *dst |= (*src & 0xF0) >> 4; dst++; } else { *dst &= 0x0F; *dst |= (*src & 0xF) << 4; src++; } } } } else { if (dx & 1) { for (pix = 0; pix < w; ++pix) { if (pix & 1) { *dst &= 0x0F; *dst |= (*src & 0xF) << 4; src++; } else { *dst &= 0xF0; *dst |= (*src & 0xF0) >> 4; dst++; } } } else { if (w & 1) { BGFX_MEMCPY(dst,src,num_bytes); dst[num_bytes] &= 0x0F; dst[num_bytes] |= (src[num_bytes] & 0xF0); } else { BGFX_MEMCPY(dst,src,num_bytes); } } } } } /**************************************************************** * INPUTS: p_src,p_dst - source and destination surface structures * sx,sy - source coordinates * dx,dy - destination coordinates * sw,sh - width and height of region * OUTPUTS: none * RETURNS: non-zero on error * FUNCTION: Blit a region of one surface to another * ****************************************************************/ int bgfx_stretch_blit(bgfx_surf_p p_src, bgfx_surf_p p_dst, uint16_t sx,uint16_t sy,uint16_t dx,uint16_t dy, uint16_t sw,uint16_t sh,uint16_t dw,uint16_t dh) { int rc = 0; if ((p_src->flags & BGFX_SURF_GRC) && (p_dst->flags & BGFX_SURF_GRC)) { void *src,*dst,*out; bgfx_rect src_r,dst_r,out_r; src = p_src->surface.surf; out = p_dst->surface.surf; dst = out; src_r.x = sx; src_r.y = sy; src_r.width = sw; src_r.height = sh; out_r.x = dst_r.x = dx; out_r.y = dst_r.y = dy; out_r.width = dst_r.width = dw; out_r.height = dst_r.height = dh; rc = bgfx_hw_blit(src,&src_r, dst, &dst_r,out, &out_r); } else if ((p_src->flags & BGFX_SURF_RGB) && (p_dst->flags & BGFX_SURF_RGB)) { /* Nearest neighbor stretch blit */ int i,j,dst_off,src_off; uint32_t fx_delta_x = (sw * 256)/dw; uint32_t fx_delta_y = (sh * 256)/dh; uint32_t *dst_ptr,*src_ptr; uint32_t m,n; src_ptr = (uint32_t*)p_src->surface.buf; dst_ptr = (uint32_t*)p_dst->surface.buf; for (i = 0; i < dh; i++) { for (j=0; j < dw; j++) { dst_off = ((p_dst->surface.pitch)/4 * (i + dy)) + dx + j; m = (((i << 8) * fx_delta_y)/256) >> 8; n = (((j << 8) * fx_delta_x)/256) >> 8; src_off = sx + ((sy + m) * (p_src->surface.pitch/4)) + n; //dst_ptr[dst_off] = src_ptr[src_off]; dst_ptr[dst_off] = bgfx_blend_argb_pixels(src_ptr[src_off],dst_ptr[dst_off]); } } } return rc; } /**************************************************************** * INPUTS: p_src,p_dst - source and destination surface structures * sx,sy - source coordinates * dx,dy - destination coordinates * sw,sh - width and height of region * OUTPUTS: none * RETURNS: non-zero on error * FUNCTION: Blit a region of one surface to another * ****************************************************************/ int bgfx_blit_rect(bgfx_surf_p p_src, bgfx_surf_p p_dst, uint16_t sx,uint16_t sy, uint16_t dx,uint16_t dy, uint16_t sw,uint16_t sh) { uint8_t *dst,*src; uint16_t i,j,w,h; uint8_t *tdst; if ((dx >= p_dst->surface.width) || (dy >= p_dst->surface.height)) { BGFX_DEBUG(("bgfx_blit_rect dst_x,dst_y (%d,%d) outside destination (%d,%d)\n",dx,dy, p_dst->surface.width,p_dst->surface.height)); return -1; } if ((sx >= p_src->surface.width) || (sy >= p_src->surface.height)) { BGFX_DEBUG(("bgfx_blit_rect src_x,src_y (%d,%d) outside source (%d,%d)\n",sx,sy, p_src->surface.width,p_src->surface.height)); return -1; } if ((p_src->flags & BGFX_SURF_GRC) && (p_dst->flags & BGFX_SURF_GRC)) { void *src,*dst,*out; bgfx_rect src_r,dst_r,out_r; int rc; src = p_src->surface.surf; out = p_dst->surface.surf; dst = out; src_r.x = sx; src_r.y = sy; src_r.width = sw; src_r.height = sh; out_r.x = dst_r.x = dx; out_r.y = dst_r.y = dy; out_r.width = dst_r.width = sw; out_r.height = dst_r.height = sh; rc = bgfx_hw_blit(src,&src_r, dst, &dst_r,out, &out_r); return rc; } src = (uint8_t*)p_src->surface.buf; src += (sy * p_src->surface.pitch) + ((sx * p_src->bpp)/8); dst = (uint8_t*)p_dst->surface.buf; dst += (dy * p_dst->surface.pitch) + ((dx * p_dst->bpp)/8); w = sw; if (sw >= (p_src->surface.width - sx)) w= p_src->surface.width - sx; if (w >= p_dst->surface.width - dx) w = p_dst->surface.width - dx; h = sh; if (sh >= (p_src->surface.height - sy)) h= p_src->surface.height - sy; if (h >= p_dst->surface.height - dy) h = p_dst->surface.height - dy; if (p_dst->flags & BGFX_SURF_RGB) { for (j = 0; j < h; ++j) { if (p_src->flags & BGFX_SURF_RGB) BGFX_MEMCPY(dst, src, w*p_src->bpp/8); else { tdst = dst; for (i = 0; i < w; ++i) { if (src[i] != 0xFF) *((uint32_t*)tdst) = bgfx_clut8_to_rgb(p_src->palette.clut,src[i]); tdst += p_dst->bpp/8; } } dst += p_dst->surface.pitch; src += p_src->surface.pitch; } } else { for (j = 0; j < h; ++j) { if (p_dst->bpp == 8) { for (i = 0; i < w; ++i) { if (p_src->palette.clut[src[i]] & 0xFF000000) { dst[i] = src[i]; } } } else { bgfx_row_copy(src,dst,sx,dx,w,p_dst->bpp); } dst += p_dst->surface.pitch; src += p_src->surface.pitch; } } bgfx_cacheflush(p_dst->surface.buf,p_dst->surface.pitch * p_dst->surface.height); return 0; } #ifdef HAS_SUBTITLE void bgfx_fill_box_subtitle( bgfx_surf_p p, /* surface buffer to draw */ uint16_t x,uint16_t y, /* x and y */ uint16_t w,uint16_t h, /* w and h */ uint8_t *pbuf, /* pointer to a buffer with bitmap for subtitle to draw */ uint16_t buf_len, /* buf length, should match the w * h * format bytes */ bgfx_pixel bg, /* background color, for palettle, it is color index */ bgfx_pixel fg, /* forground color, for palettle, it is color index */ bool update_bg /* update backgound with given color */ ) { register uint8_t *dst; register uint16_t i, j; register int16_t shift; #ifdef CONFIG_BGFX_SURF_RGB uint8_t *tdst; #endif if (y + h > p->surface.height) h = p->surface.height - y; /* TODO we shouldn't make modification to in horizontal size otherwise it may not match bitmap passed */ if (w + x > p->surface.width) w = p->surface.width - x; dst = (uint8_t*)p->surface.buf + (y * p->surface.pitch + ((x * p->bpp) >> 3)); shift = 7; #ifdef CONFIG_BGFX_SURF_RGB int flags = p->flags; if (p->bpp <= 8) { /* clear bit so that we can reuse code */ flags &= ~BGFX_SURF_RGB; } if (flags & BGFX_SURF_RGB) { uint32_t cfg, cbg; cfg = fg; cbg = bg; cfg = BPXL_MAKE_PIXEL(bgfx_colorformat_mapping(p->format), (cfg>>24) & 0xff, (cfg>>16)&0xff, (cfg>>8)&0xff, cfg&0xff); cbg = BPXL_MAKE_PIXEL(bgfx_colorformat_mapping(p->format), (cbg>>24) & 0xff, (cbg>>16)&0xff, (cbg>>8)&0xff, cbg&0xff); /* if all zero in forground color, modified it to avoid same color as clear */ if (0x8000 == cfg) cfg = 0x8421; if (32 == p->bpp) { for (j = 0; j < h; j++) { tdst = dst; for (i = 0; (i < w) && ((int)buf_len > 0); i++, tdst += 4) { if (*pbuf & (1 << shift)) *((uint32_t*)tdst) = cfg; else { if (update_bg) *((uint32_t*)tdst) = cbg; } if (shift <= 0) { shift = 7; pbuf++; buf_len--; } else shift--; } dst += p->surface.pitch; } } else { /* 16 bits */ for (j = 0; j < h; j++) { tdst = dst; for (i = 0; (i < w) && ((int)buf_len > 0); i++, tdst += 2) { if (*pbuf & (1 << shift)) *((uint16_t*)tdst) = (uint16_t)cfg; else { if (update_bg) *((uint16_t*)tdst) = (uint16_t)cbg; } if (shift <= 0) { shift = 7; pbuf++; buf_len--; } else shift--; } dst += p->surface.pitch; } } } else #endif { uint8_t c; /* for performance */ if (p->format == bgraphics_pixel_format_palette4) { /* special case for P4 */ dst = (uint8_t*)p->surface.buf + y * p->surface.pitch; fg &= 0x0f; bg &= 0x0f; } else if (p->format != bgraphics_pixel_format_palette4) return; for (j = 0; j < h; j++) { if(p->format == bgraphics_pixel_format_palette4) { for (i = x; (i < (w + x)) && ((int)buf_len > 0); i++) { if (*pbuf & (1 << shift)) c = (uint8_t)fg; else { if (update_bg) c = (uint8_t)bg; else { goto SKIP_IT; } } if (i & 1) { *(dst + (i >> 1)) &= 0xF0; *(dst + (i >> 1)) |= c; } else { *(dst + (i >> 1)) &= 0x0F; *(dst + (i >> 1)) |= (c << 4); } SKIP_IT: if (shift <= 0) { shift = 7; pbuf++; buf_len--; } else shift--; } } else { for (i = 0; (i < w) && ((int)buf_len > 0); i++) { if (*pbuf & (1 << shift)) *(dst + i) = (uint8_t)fg; else { if (update_bg) *(dst + i) = (uint8_t)bg; } if (shift <= 0) { shift = 7; pbuf++; buf_len--; } else shift--; } } dst += p->surface.pitch; } } bgfx_cacheflush(p->surface.buf,p->surface.pitch * p->surface.height); } #endif /**************************************************************** * bgfx_draw_text_ex - extended version of draw_text; draws text * within a bounded area, and returns the actual text * dimensions * * INPUTS: p - surface structure * x,y - coordinates * width,height - width and height of the text box to draw * str_p - array of 32-bit unicode characters * str_len - number of 32-bit characters in array * font_p - font definition * fg - foreground color * line_spacing - line space * justify - 0=left, 1=center, 2=right; default is left * render - true to render text, false to just size it * OUTPUTS: width,height - actual max width and height of text * RETURNS: true if text fits, false if text does not fit * ****************************************************************/ #define max_uint16 0xFFFF bool bgfx_draw_text_ex( bgfx_surf_p p, /* pointer to initialized surface structure */ uint16_t x, /* horizontal location in pixels (top/left 0,0) */ uint16_t y, /* vertical location in pixels */ uint16_t *width, /* horizontal size in pixels (top/left 0,0) */ uint16_t *height, /* vertical size in pixels */ const unsigned int *str_p, /* array of 32-bit unicode characters */ uint16_t str_len, /* length of character array */ bgfx_font_t *font_p, /* initialized font structure (see sgl_font.h) */ bgfx_pixel fg, /* foreground color */ int line_spacing, /* leading (extra space between lines) */ uint16_t justify, /* justification type: 0=left, 1=center, 2=right */ bool render /* true to render the text, false to just size it */ ) { uint16_t offset, bi, bw, bstop, line_idx, last_line, ch_w, ch_nw, bot, h, rh, max_w, cc, adv; static bgfx_glyph_t * p_bgfx_glyph[MAX_TEXT_BOX_CHARS]; static lineinfo_t ln[MAX_LINES]; bool rc = true; /* limit size of text */ if (str_len > MAX_TEXT_BOX_CHARS-1) { str_len = MAX_TEXT_BOX_CHARS-1; } bot = y + *height; /* calculate bottom boundary of text box */ max_w = ch_w = line_idx = cc = 0; /* init max width, line width, line index, char count (per line) */ ln[0].e = max_uint16; /* bounds of first line not yet known */ bi = 0; /* -------------------------------------------------- */ /* first pass: determine where lines begin and end */ /* -------------------------------------------------- */ for (offset = 0; offset < str_len; offset++) { /* for each 32-bit character in the "string" */ /* set up some shorthand */ unsigned long uc = str_p[offset]; /* get 32-bit char */ char c = (char)(uc & 0xFF); /* assume char is actually ascii */ if (uc & 0xFFFFFF00) c = 0; /* if not, clear ascii char */ if ((c < 0x20) || (c > 0x7E)) c = 0; /* only printable ascii, actually */ /* get glyph info for next character */ p_bgfx_glyph[offset] = bgfx_get_glyph(font_p,uc); /* get glyph data for next character */ if (p_bgfx_glyph[offset] == NULL) { /* if unprintable */ p_bgfx_glyph[offset] = bgfx_get_glyph(font_p,s_space_char); /* substitute a space character */ } adv = p_bgfx_glyph[offset]->advance; /* get width of glyph */ if (c == 0) { /* if not printable ascii */ BGFX_DEBUG(("str[%d]=0x%08lX glyph: left=%d,top=%d,adv=%d,width=%d,height=%d,pitch=%d\n", offset,uc, p_bgfx_glyph[offset]->left,p_bgfx_glyph[offset]->top,adv, p_bgfx_glyph[offset]->width,p_bgfx_glyph[offset]->height,p_bgfx_glyph[offset]->pitch)); } else { BGFX_DEBUG(("str[%d]='%c' glyph: left=%d,top=%d,adv=%d,width=%d,height=%d,pitch=%d\n", offset,c, p_bgfx_glyph[offset]->left,p_bgfx_glyph[offset]->top,adv, p_bgfx_glyph[offset]->width,p_bgfx_glyph[offset]->height,p_bgfx_glyph[offset]->pitch)); } /* check for line boundaries */ ch_nw = ch_w + adv; /* calculate potential new width */ if (uc == s_null_char) { /* if this is a null */ BGFX_DEBUG(("Ignoring null char at offset %d\n",offset)); /* just ignore it */ } else if (uc == s_newline_char) { /* if this is a forced newline */ ln[line_idx].e = offset; /* note line is terminated */ ln[line_idx].w = ch_w; /* note length of this line */ ln[line_idx].c = cc; /* save count of chars on this line (not including newline char) */ ch_w = cc = 0; /* set starting width and char count for next line */ } else if (ch_nw <= *width) { /* if char fits on this line */ ch_w = ch_nw; /* add advance */ BGFX_DEBUG(("char %d fits; ch_w is now %d\n",cc,ch_w)); cc++; /* count this char */ } else { /* char does NOT fit on current line */ bstop = (line_idx == 0) ? 1 : ln[line_idx-1].e + 1; /* where to stop - start of line or whole string */ /* see if there's a word space that can serve as the breakpoint */ bw = 0; /* to accumulate widths of backed-off chars */ bi = offset; /* start scanning back from current character */ while (bi > bstop) { /* while there are still chars to check */ if (str_p[bi] == s_space_char) { /* if we can break on this character */ break; /* break out of the loop */ } bw += p_bgfx_glyph[bi]->advance; /* add width of this char (for next line) */ BGFX_DEBUG(("Backing up over char 0x%08X at offset %d, bw is now %d\n",str_p[bi],bi,bw)); bi--; /* back up to prior char */ } if (bi > bstop) { /* if space found */ /* set initial metrics for next line */ ch_w = bw; /* set starting width for next line (drop space) */ cc = offset - bi; /* set starting char count for next line */ /* trim all trailing spaces at end of line so that justification works right */ while ((bi > bstop) && (str_p[bi] == s_space_char)) { /* for all trailing spaces on this line */ bw += p_bgfx_glyph[bi]->advance; /* add its width to the stuff being removed from this line */ BGFX_DEBUG(("Backing up over space char at offset %d, bw is now %d\n",str_p[bi],bi,bw)); bi--; /* back up over it */ } ln[line_idx].e = bi+1; /* set first space as end of current line */ ln[line_idx].w = ch_nw - bw; /* adjust line width to just the chars now on this line */ ln[line_idx].c = cc - (offset - bi+1); /* set char count for this line */ } /* if no breaking word space found, try for a hyphen */ if (ln[line_idx].e == max_uint16) { /* no breaking space found */ bw = 0; /* to accumulate widths of backed-off chars */ bi = offset-1; /* start scanning back from previous character */ while (bi > bstop) { /* while there are still chars to check */ if (str_p[bi] == s_hyphen_char) { /* if we can break on this character */ ln[line_idx].e = bi; /* set as end of current line */ ln[line_idx].w = ch_w - bw; /* adjust line width (includes hyphen) */ ln[line_idx].c = cc - (offset - bi); /* set char count for this line */ ch_w = bw + adv; /* set starting width for next line */ cc = offset - bi; /* set starting char count for next line */ bi = 0; /* to stop loop now */ break; } bw += p_bgfx_glyph[bi]->advance; /* add width of this char */ bi--; /* back up to prior char */ } } /* if neither a space nor hyphen found to break on, force break at current character (move to next line) */ if (ln[line_idx].e == max_uint16) { /* if still no breaking character found */ ln[line_idx].e = offset-1; /* mark previous char as last on this line */ ln[line_idx].w = ch_w; /* set length of this line */ ln[line_idx].c = cc; /* set char count, not including current char */ ch_w = adv; /* set starting width for next line */ cc = 1; /* this char is already on next line */ } } /* if line finished off, move to next line */ if (ln[line_idx].e != max_uint16) { /* if current line has been finished off */ if (ln[line_idx].w > max_w) { /* if longest line so far */ max_w = ln[line_idx].w; /* update max width */ } line_idx++; /* advance line index */ ln[line_idx].e = max_uint16; /* end of line not yet known */ } if (line_idx >= (MAX_LINES - 1)) { break; } } /* if there's data on the last line, finish it off */ if (ch_w > 0) { /* if there's data there */ ln[line_idx].e = str_len-1; /* terminate the last line at the last char */ ln[line_idx].w = ch_w; /* note length */ ln[line_idx].c = cc; /* set char count */ if (ln[line_idx].w > max_w) { /* if last line is longest */ max_w = ln[line_idx].w; /* update max width */ } line_idx++; /* count it */ } last_line = line_idx; /* -------------------------------------------------- */ /* second pass: determine line heights and total height */ /* -------------------------------------------------- */ line_idx = h = rh = 0; /* init line index, line height, running height */ for (offset = 0; offset < str_len; offset++) { /* for each char */ if (p_bgfx_glyph[offset]->height > h) { /* if this glyph's height is largest on this line so far */ #ifdef BGFX_VARIABL_LINE_HEIGHT h = p_bgfx_glyph[offset]->height; /* note the largest glyph on this line */ #else h = font_p->height; /* note the largest glyph on this line */ #endif } if (offset == ln[line_idx].e) { /* if this is the end of the line */ ln[line_idx].h = h; /* store line height */ if (rh != 0) rh += line_spacing; /* add leading (space between lines) */ rh += h; /* add line height */ if (rh > bot) { /* if exceeded vertical size of text box */ last_line = line_idx; /* update last line number */ rc = false; /* note so we can return indication */ break; /* bail out of pass 2 loop now */ } line_idx++; /* advance to next line */ h = 0; /* reset line height */ } } /* -------------------------------------------------- */ /* last pass: render the text */ /* -------------------------------------------------- */ if (render) { /* if text is to actually be drawn */ ch_w = line_idx = 0; /* init line width, line index */ h = font_p->ascender; for (offset = 0; offset < str_len; offset++) { /* for each char */ unsigned long uc = str_p[offset]; /* get 32-bit char */ char c = (char)(uc & 0xFF); /* assume char is actually ascii */ if (uc & 0xFFFFFF00) c = 0; /* if not, clear ascii char */ if ((c < 0x20) || (c > 0x7E)) c = 0; /* only printable ascii, actually */ /* at beginning of line, determine starting horizontal offset */ if (ch_w == 0) { /* if start of line */ switch (justify) { /* depending on justification mode */ case 1: /* to center text */ bi = (*width - ln[line_idx].w)/2; /* divide extra space in half */ break; case 2: /* to right-justify text */ bi = *width - ln[line_idx].w; /* place all extra space at left */ break; default: bi = 0; /* all extra space is at right */ break; } /* switch */ } /* at end of line, render last char if needed and set up for next line */ if (offset == ln[line_idx].e) { /* if this is the end of the line */ /* if other than null or newline, last char must be rendered */ if ((str_p[offset] != s_null_char) && (str_p[offset] != s_newline_char)) { if (c == 0) { BGFX_DEBUG(("Rendering char %d (0x%08lX) at (%d,%d) - last glyph on line\n",offset,uc,x+ch_w+bi,y+h)); } else { BGFX_DEBUG(("Rendering char %d (%c) at (%d,%d) - last glyph on line\n",offset,c,x+ch_w+bi,y+h)); } bgfx_render_glyph(p,x+ch_w+bi,y+h,str_p[offset],font_p,fg,p_bgfx_glyph[offset],eBGFX_NORMAL); } else { BGFX_DEBUG(("Last glyph on line is null, space, or newline\n",offset,uc,x+ch_w+bi,y+h)); } h += line_spacing + ln[line_idx].h; /* add leading plus height of current line */ line_idx++; /* advance to next line */ ch_w = 0; /* reset horizontal position */ if (line_idx == last_line) break; /* don't exceed bottom boundary of text box */ } else if (uc != s_null_char) { /* render non-null glyph and add width to running line width */ if (c == 0) { BGFX_DEBUG(("Rendering char %d (0x%08lX) at (%d,%d)\n",offset,uc,x+ch_w+bi,y+h)); } else { BGFX_DEBUG(("Rendering char %d (%c) at (%d,%d)\n",offset,c,x+ch_w+bi,y+h)); } ch_w += bgfx_render_glyph(p,x+ch_w+bi,y+h,str_p[offset],font_p,fg,p_bgfx_glyph[offset],eBGFX_NORMAL); } } bgfx_cacheflush(p->surface.buf,p->surface.pitch * p->surface.height); } /* return required info */ *width = max_w; *height = rh; return rc; } /**************************************************************** * bgfx_draw_text_box_ex - extended version of draw_text_box; * draws specified text in bounded box, with specified * background and border colors * * INPUTS: p - surface structure * x,y - coordinates * width,height - maximum width and height of text box * str_p - array of 32-bit unicode characters * str_len - number of 32-bit characters in array * f - font definition * fg - foreground color * bg - background color * bc - border color * line_spacing - line space * m - text margin, in pixels * b - border width, in pixels; 0 = no border * justify - 0=left, 1=center, 2=right; default is left * OUTPUTS: none * RETURNS: true if text fits in box, false if not * ****************************************************************/ bool bgfx_draw_text_box_ex( bgfx_surf_p p, /* pointer to initialized surface structure */ uint16_t *x, /* horizontal location in pixels (top/left 0,0)*/ uint16_t *y, /* vertical location in pixels */ uint16_t *w, /* horizontal size in pixels (top/left 0,0) */ uint16_t *h, /* vertical size in pixels */ const unsigned int *str_p, /* array of 32-bit unicode characters */ uint16_t str_len, /* length of character array */ bgfx_font_t *f, /* initialized font structure (see sgl_font.h) */ bgfx_pixel fg, /* foreground color */ bgfx_pixel bg, /* background color */ bgfx_pixel bc, /* border color */ uint16_t line_spacing, /* leading (extra space between lines) */ uint16_t m, /* margin between text and borders */ uint16_t b, /* width of border (on the outside of the box) */ uint16_t h_justify, /* Horizontal justification type: 0=left, 1=center, 2=right */ uint16_t v_justify /* Vertical justification type:0=top, 1=center */ ) { uint16_t margin,width,height,horadj,veradj,left,top; int i; bool res = true; margin = m + m; /* total margin (top and bottom or left and right) */ if ((*x < b) || (*y < b)) { /* if origin doesn't allow room for borders on left or top */ return false; } if (((*x + *w) > (p->surface.width - b)) || ((*y + *h) > (p->surface.height - b))) { /* if origin + size doesn't allow room for borders on right or bottom */ return false; } if ((*w <= margin) || (*h <= margin)) { /* if not enough room for any text between margins */ return false; } /* determine initial bounds of text area */ left = *x + m; top = *y + m; /* add text margin to box origin */ width = *w - margin; /* adjust dimensions so text area is inside margins */ height = *h - margin; /* size up the text vertically and horizontally */ if (!bgfx_draw_text_ex(p, /* graphics surface */ left, top, /* box left and top */ &width, &height, /* box width and height */ str_p, str_len, /* text string and character count */ f, /* font */ fg, /* foreground color */ line_spacing, /* vertical line spacing */ h_justify, /* horizontal justification */ false)) /* don't render on this pass */ { res = false; } /* adjust box dimensions based on sized text */ horadj = (*w - margin) - width; /* horizontal adjustment */ veradj = (*h - margin) - height; /* vertical adjustment */ *x += horadj/2; *w -= horadj; /* to center box horizontally */ *y += veradj/2; *h -= veradj; /* to center box vertically */ /* draw screen background based on resulting size */ bgfx_fill_rect(p,*x,*y,*w,*h,bg); /* fill in box with background color */ for (i = 0; i < b; i++) { /* now draw the borders */ BGFX_DEBUG((" top border (x1,x2,y)=(%d,%d,%d)\n",*x-b+i,*x+b+*w-i,*y-b+i)); bgfx_h_draw_line(p,*x-b+i,*x+b+*w-i,*y-b+i,bc); /* top border */ BGFX_DEBUG(("bottom border (x1,x2,y)=(%d,%d,%d)\n",*x-b+i,*x+b+*w-i,*y+b+*h-1-i)); bgfx_h_draw_line(p,*x-b+i,*x+b+*w-i,*y+b+*h-1-i,bc); /* bottom border */ BGFX_DEBUG((" left border (x,y1,y2)=(%d,%d,%d)\n",*x-b+i,*y-b+i,*y+b+*h-1-i)); bgfx_v_draw_line(p,*x-b+i,*y-b+i,*y+b+*h-1-i,bc); /* left border */ BGFX_DEBUG((" right border (x,y1,y2)=(%d,%d,%d)\n",*x+b+*w-i-1,*y-b+i,*y+b+*h-1-i)); bgfx_v_draw_line(p,*x+b+*w-i-1,*y-b+i,*y+b+*h-1-i,bc); /* right border */ } if ( v_justify != 0 ) { left = *x + m; /* to center horizontally */ top = *y + m; /* to center vertically */ } /* render the text, centered in the box */ bgfx_draw_text_ex( p, /* graphics surface */ left, top, /* box left and top */ &width, &height, /* box width and height */ str_p, str_len, /* text string and character count */ f, /* font */ fg, /* foreground color */ line_spacing, /* vertical line spacing */ h_justify, /* horizontal justification */ true); /* render text on second pass */ return res; }