📄 gameswf_fontlib.cpp
字号:
int iy0 = (int) ceilf(y0); int iy1 = (int) ceilf(y1); float dy = y1 - y0; for (int y = iy0; y < iy1; y++) { if (y < 0) continue; if (y >= s_glyph_render_size) return; float f = (y - y0) / dy; int xl = (int) ceilf(flerp(xl0, xl1, f)); int xr = (int) ceilf(flerp(xr0, xr1, f)); xl = iclamp(xl, 0, s_glyph_render_size - 1); xr = iclamp(xr, 0, s_glyph_render_size - 1); if (xr > xl) { memset(s_render_buffer + y * s_glyph_render_size + xl, 255, xr - xl); } } } struct draw_into_software_buffer : tesselate::trapezoid_accepter // A trapezoid accepter that does B&W rendering into our // software buffer. { // Overrides from trapezoid_accepter virtual void accept_trapezoid(int style, const tesselate::trapezoid& tr) { // Transform the coords. float x_scale = s_render_matrix.m_[0][0]; float y_scale = s_render_matrix.m_[1][1]; float x_offset = s_render_matrix.m_[0][2]; float y_offset = s_render_matrix.m_[1][2]; float y0 = tr.m_y0 * y_scale + y_offset; float y1 = tr.m_y1 * y_scale + y_offset; float lx0 = tr.m_lx0 * x_scale + x_offset; float lx1 = tr.m_lx1 * x_scale + x_offset; float rx0 = tr.m_rx0 * x_scale + x_offset; float rx1 = tr.m_rx1 * x_scale + x_offset; // Draw into the software buffer. software_trapezoid(y0, y1, lx0, lx1, rx0, rx1); } virtual void accept_line_strip(int style, const point coords[], int coord_count) { assert(0); // Shape glyphs should not contain lines. } }; static bool render_glyph(rendered_glyph_info* rgi, const shape_character_def* sh) // Render the given outline shape into a cached font texture. // Return true if the glyph is not empty; false if it's // totally empty. // // Return fill in the image and offset members of the given // rgi. { assert(rgi); assert(sh); assert(s_render_buffer); // // Tesselate and render the shape into a software buffer. // // Clear the render output to 0. memset(s_render_buffer, 0, s_glyph_render_size * s_glyph_render_size); // Look at glyph bounds; adjust origin to make sure // the shape will fit in our output. float offset_x = 0.f; float offset_y = s_rendering_box; rect glyph_bounds; sh->compute_bound(&glyph_bounds); if (glyph_bounds.m_x_min < 0) { offset_x = - glyph_bounds.m_x_min; } if (glyph_bounds.m_y_max > 0) { offset_y = s_rendering_box - glyph_bounds.m_y_max; } s_render_matrix.set_identity(); s_render_matrix.concatenate_scale(s_glyph_render_size / s_rendering_box); s_render_matrix.concatenate_translation(offset_x, offset_y); // Tesselate & draw the shape. draw_into_software_buffer accepter; sh->tesselate(s_rendering_box / s_glyph_render_size * 0.5f, &accepter); // // Process the results of rendering. // // Shrink the results down by a factor of 4x, to get // antialiasing. Also, analyze the data boundaries. bool any_nonzero_pixels = false; int min_x = s_glyph_nominal_size; int max_x = 0; int min_y = s_glyph_nominal_size; int max_y = 0; Uint8* output = new Uint8[s_glyph_nominal_size * s_glyph_nominal_size]; for (int j = 0; j < s_glyph_nominal_size; j++) { for (int i = 0; i < s_glyph_nominal_size; i++) { // Sum up the contribution to this output texel. int sum = 0; for (int jj = 0; jj < OVERSAMPLE_FACTOR; jj++) { for (int ii = 0; ii < OVERSAMPLE_FACTOR; ii++) { Uint8 texel = s_render_buffer[ ((j << OVERSAMPLE_BITS) + jj) * s_glyph_render_size + ((i << OVERSAMPLE_BITS) + ii)]; sum += texel; } } sum >>= OVERSAMPLE_BITS; sum >>= OVERSAMPLE_BITS; if (sum > 0) { any_nonzero_pixels = true; min_x = imin(min_x, i); max_x = imax(max_x, i); min_y = imin(min_y, j); max_y = imax(max_y, j); } output[j * s_glyph_nominal_size + i] = (Uint8) sum; } } if (any_nonzero_pixels) { // Fill in rendered_glyph_info. rgi->m_image = new image::alpha(max_x - min_x + 1, max_y - min_y + 1); rgi->m_offset_x = offset_x / s_rendering_box * s_glyph_nominal_size - min_x; rgi->m_offset_y = offset_y / s_rendering_box * s_glyph_nominal_size - min_y; // Copy the rendered glyph into the new image. {for (int j = 0, n = rgi->m_image->m_height; j < n; j++) { memcpy( image::scanline(rgi->m_image, j), output + (min_y + j) * s_glyph_nominal_size + min_x, rgi->m_image->m_width); }} } else { // Glyph is empty; don't create an image for it. return false; } delete [] output; // @@ TODO should keep this around longer, instead of new/delete for each glyph rgi->m_image_hash = rgi->m_image->compute_hash(); return true; } bool try_to_reuse_previous_image( const rendered_glyph_info& rgi, const hash<unsigned int, const rendered_glyph_info*>& image_hash) // See if we've already packed an identical glyph image for // another glyph. If so, then reuse it, and return true. // If no reusable image, return false. // // Reusing identical images can be a huge win, especially for // fonts that use the same dummy glyph for many undefined // characters. { const rendered_glyph_info* identical_image = NULL; if (image_hash.get(rgi.m_image_hash, &identical_image)) { // Found a match. But is it *really* a match? Do a // bitwise compare. if (*(rgi.m_image) == *(identical_image->m_image)) { // Yes, a real bitwise match. Use the previous // image's texture data. texture_glyph identical_tg = identical_image-> m_source_font-> get_texture_glyph(identical_image->m_glyph_index); if (identical_tg.is_renderable() == false) { // The matching glyph hasn't been pushed into the font yet. // Search for it in s_pending_glyphs. bool found_it = false; for (int i = 0, n = s_pending_glyphs.size(); i < n; i++) { const pending_glyph_info& pgi = s_pending_glyphs[i]; if (pgi.m_source_font == identical_image->m_source_font && pgi.m_glyph_index == identical_image->m_glyph_index) { // This is the one we want to alias with. identical_tg = pgi.m_texture_glyph; found_it = true; } } if (found_it == false) { // Should not happen -- glyph should either be in the font, or in s_pending_glyphs. assert(0); return false; } } texture_glyph tg; // copy the bitmap & uv data from identical_tg tg = identical_tg; // Use our own offset, in case it's different. tg.m_uv_origin.m_x = tg.m_uv_bounds.m_x_min + rgi.m_offset_x / GLYPH_CACHE_TEXTURE_SIZE; tg.m_uv_origin.m_y = tg.m_uv_bounds.m_y_min + rgi.m_offset_y / GLYPH_CACHE_TEXTURE_SIZE; if (identical_tg.is_renderable()) { // This image is already packed and has a valid bitmap_info. // Push straight into our font. rgi.m_source_font->add_texture_glyph(rgi.m_glyph_index, tg); } else { // Set bitmap_info and push into font once texture is done being packed. s_pending_glyphs.push_back( pending_glyph_info( rgi.m_source_font, rgi.m_glyph_index, tg)); } return true; } // else hash matched, but images didn't. } else {#if 0#ifndef NDEBUG // Sanity check the hash -- there should be no // image in it that exactly matches this // image. for (hash<unsigned int, const rendered_glyph_info*>::const_iterator it = image_hash.begin(); it != image_hash.end(); ++it) { if (*(rgi.m_image) == *(it->second->m_image)) { // bah! what up??? unsigned int hash_a = rgi.m_image->compute_hash(); unsigned int hash_b = it->second->m_image->compute_hash(); log_msg("a = %x, b = %x\n", hash_a, hash_b);//xxxxx } }#endif // not NDEBUG#endif // 0 } return false; } void pack_and_assign_glyphs(array<rendered_glyph_info>* glyph_info, movie_definition_sub* owner) // Pack the given glyphs into textures, and push the // texture_glyph info into the source fonts. // // Re-arranges the glyphs (i.e. sorts them by size) but // otherwise doesn't munge the array. { // Sort the glyphs by size (biggest first). struct sorter { static int sort_by_size(const void* a, const void* b) // For qsort. { const rendered_glyph_info* ga = (const rendered_glyph_info*) a; const rendered_glyph_info* gb = (const rendered_glyph_info*) b; int a_size = ga->m_image->m_width + ga->m_image->m_height; int b_size = gb->m_image->m_width + gb->m_image->m_height; return b_size - a_size; } }; if (glyph_info->size()) { qsort(&(*glyph_info)[0], glyph_info->size(), sizeof((*glyph_info)[0]), sorter::sort_by_size); } // Flag for whether we've processed this glyph yet. array<bool> packed; packed.resize(glyph_info->size()); for (int i = 0, n = packed.size(); i < n; i++) { packed[i] = false; } // Share identical texture data where possible, by // doing glyph image comparisons. hash<unsigned int, const rendered_glyph_info*> image_hash; // Pack the glyphs. {for (int i = 0, n = glyph_info->size(); i < n; ) { int index = i; // Try to pack a glyph into the existing texture. for (;;) { const rendered_glyph_info& rgi = (*glyph_info)[index]; // First things first: are we identical to a glyph that has // already been packed? if (try_to_reuse_previous_image(rgi, image_hash)) { packed[index] = true; break; } int raw_width = rgi.m_image->m_width; int raw_height = rgi.m_image->m_height; // Need to pad around the outside. int width = raw_width + (PAD_PIXELS * 2); int height = raw_height + (PAD_PIXELS * 2); assert(width < GLYPH_CACHE_TEXTURE_SIZE); assert(height < GLYPH_CACHE_TEXTURE_SIZE); // Does this glyph fit? int pack_x = 0, pack_y = 0; ensure_cache_image_available(); if (pack_rectangle(&pack_x, &pack_y, width, height)) { // Fits! // Blit the output image into its new spot. for (int j = 0; j < raw_height; j++) { memcpy(s_current_cache_image + (pack_y + PAD_PIXELS + j) * GLYPH_CACHE_TEXTURE_SIZE + pack_x + PAD_PIXELS, image::scanline(rgi.m_image, j), raw_width); } // Fill out the glyph info. texture_glyph tg; tg.m_uv_origin.m_x = (pack_x + rgi.m_offset_x) / (GLYPH_CACHE_TEXTURE_SIZE); tg.m_uv_origin.m_y = (pack_y + rgi.m_offset_y) / (GLYPH_CACHE_TEXTURE_SIZE); tg.m_uv_bounds.m_x_min = float(pack_x) / (GLYPH_CACHE_TEXTURE_SIZE); tg.m_uv_bounds.m_x_max = float(pack_x + width) / (GLYPH_CACHE_TEXTURE_SIZE); tg.m_uv_bounds.m_y_min = float(pack_y) / (GLYPH_CACHE_TEXTURE_SIZE); tg.m_uv_bounds.m_y_max = float(pack_y + height) / (GLYPH_CACHE_TEXTURE_SIZE); // Fill in bitmap info and push into the source font later. s_pending_glyphs.push_back( pending_glyph_info( rgi.m_source_font, rgi.m_glyph_index, tg)); // Add this into the hash so it can possibly be reused. if (image_hash.get(rgi.m_image_hash, NULL) == false) { image_hash.add(rgi.m_image_hash, &rgi); } packed[index] = true; break; } else { // Try the next unpacked glyph. index++; while (index < n && packed[index]) index++; if (index >= n) { // None of the glyphs will fit. Finish off this texture. finish_current_texture(owner); // And go around again. index = i; } } } // Skip to the next unpacked glyph. while (i < n && packed[i]) i++; }} } static void generate_font_bitmaps(array<rendered_glyph_info>* glyph_info, font* f, movie_definition_sub* owner) // Render images for each of the font's glyphs, and put the // info about them in the given array. { assert(glyph_info); assert(f); f->set_texture_glyph_nominal_size(s_glyph_nominal_size); for (int i = 0, n = f->get_glyph_count(); i < n; i++) { if (f->get_texture_glyph(i).is_renderable() == false) { shape_character_def* sh = f->get_glyph(i); if (sh) { rect glyph_bounds; sh->compute_bound(&glyph_bounds); if (glyph_bounds.width() < 0) { // Invalid width; this must be an empty glyph. // Don't bother generating a texture for it. } else { // Add a glyph. rendered_glyph_info rgi; rgi.m_source_font = f; rgi.m_glyph_index = i; if (render_glyph(&rgi, sh) == true) { glyph_info->push_back(rgi); } // else glyph is empty } } } } } float get_texture_glyph_max_height(const font* f) { return 1024.0f / s_rendering_box * f->get_texture_glyph_nominal_size(); // s_glyph_nominal_size; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -