font_t* load_font(const char* filename) { image_t* atlas = NULL; int atlas_x, atlas_y; int atlas_size_x, atlas_size_y; sfs_file_t* file; font_t* font = NULL; struct font_glyph* glyph; struct rfn_glyph_header glyph_hdr; long glyph_start; uint8_t* grayscale; image_lock_t* lock = NULL; int max_x = 0, max_y = 0; int min_width = INT_MAX; int64_t n_glyphs_per_row; int pixel_size; struct rfn_header rfn; uint8_t *psrc; color_t *pdest; int i, x, y; console_log(2, "loading font #%u as `%s`", s_next_font_id, filename); memset(&rfn, 0, sizeof(struct rfn_header)); if ((file = sfs_fopen(g_fs, filename, NULL, "rb")) == NULL) goto on_error; if (!(font = calloc(1, sizeof(font_t)))) goto on_error; if (sfs_fread(&rfn, sizeof(struct rfn_header), 1, file) != 1) goto on_error; pixel_size = (rfn.version == 1) ? 1 : 4; if (!(font->glyphs = calloc(rfn.num_chars, sizeof(struct font_glyph)))) goto on_error; // pass 1: load glyph headers and find largest glyph glyph_start = sfs_ftell(file); for (i = 0; i < rfn.num_chars; ++i) { glyph = &font->glyphs[i]; if (sfs_fread(&glyph_hdr, sizeof(struct rfn_glyph_header), 1, file) != 1) goto on_error; sfs_fseek(file, glyph_hdr.width * glyph_hdr.height * pixel_size, SFS_SEEK_CUR); max_x = fmax(glyph_hdr.width, max_x); max_y = fmax(glyph_hdr.height, max_y); min_width = fmin(min_width, glyph_hdr.width); glyph->width = glyph_hdr.width; glyph->height = glyph_hdr.height; } font->num_glyphs = rfn.num_chars; font->min_width = min_width; font->max_width = max_x; font->height = max_y; // create glyph atlas n_glyphs_per_row = ceil(sqrt(rfn.num_chars)); atlas_size_x = max_x * n_glyphs_per_row; atlas_size_y = max_y * n_glyphs_per_row; if ((atlas = create_image(atlas_size_x, atlas_size_y)) == NULL) goto on_error; // pass 2: load glyph data sfs_fseek(file, glyph_start, SFS_SEEK_SET); if (!(lock = lock_image(atlas))) goto on_error; for (i = 0; i < rfn.num_chars; ++i) { glyph = &font->glyphs[i]; if (sfs_fread(&glyph_hdr, sizeof(struct rfn_glyph_header), 1, file) != 1) goto on_error; atlas_x = i % n_glyphs_per_row * max_x; atlas_y = i / n_glyphs_per_row * max_y; switch (rfn.version) { case 1: // RFN v1: 8-bit grayscale glyphs if (!(glyph->image = create_subimage(atlas, atlas_x, atlas_y, glyph_hdr.width, glyph_hdr.height))) goto on_error; grayscale = malloc(glyph_hdr.width * glyph_hdr.height); if (sfs_fread(grayscale, glyph_hdr.width * glyph_hdr.height, 1, file) != 1) goto on_error; psrc = grayscale; pdest = lock->pixels + atlas_x + atlas_y * lock->pitch; for (y = 0; y < glyph_hdr.height; ++y) { for (x = 0; x < glyph_hdr.width; ++x) pdest[x] = color_new(psrc[x], psrc[x], psrc[x], 255); pdest += lock->pitch; psrc += glyph_hdr.width; } break; case 2: // RFN v2: 32-bit truecolor glyphs if (!(glyph->image = read_subimage(file, atlas, atlas_x, atlas_y, glyph_hdr.width, glyph_hdr.height))) goto on_error; break; } } unlock_image(atlas, lock); sfs_fclose(file); free_image(atlas); font->id = s_next_font_id++; return ref_font(font); on_error: console_log(2, "failed to load font #%u", s_next_font_id++); sfs_fclose(file); if (font != NULL) { for (i = 0; i < rfn.num_chars; ++i) { if (font->glyphs[i].image != NULL) free_image(font->glyphs[i].image); } free(font->glyphs); free(font); } if (lock != NULL) unlock_image(atlas, lock); if (atlas != NULL) free_image(atlas); return NULL; }
/* An function helping to filter create new image. * It should be called by filter in inicialization. Filter passes * width,height,pixelwidth, pixelheight * and palette he wants to pass to his child and flags defining how it works * with image(IMAGEDATA if it requires data from previous frames (like blur * filter, TOUCHIMAGE if it changes data in image(like blur or stereogram * filter but unlike interlace and NEWIMAGE if it strictly requires to create * new image) * As palette he should pass NULL to keep parents palette. Same as * (pixel)width/height should be passed 0; * * Function then aplies some heruistic in order to minimize memory * requirements. So it should share image, create image that shares image data * or create new image) * * fills f->image, f->childimage and returns 1 if sucess and 0 if fail(usually * out of memory or it is unable to fit child's requirements) * and prepares data for child call. */ int inherimage(struct filter *f, struct initdata *data, int flags, int width, int height, struct palette *palette, float pixelwidth, float pixelheight) { int newimage = 0; int subimage = 1; int sharedimage = 1; struct image *i; int ddatalost = 0; if (width == 0) width = data->image->width; if (height == 0) height = data->image->height; #ifdef DEBUG printf("Inherimage:%s %i %i imagedata:%i %i\n", f->name, width, height, flags & IMAGEDATA, flags & PROTECTBUFFERS); #endif if (pixelwidth == 0) pixelwidth = data->image->pixelwidth; if (pixelheight == 0) pixelheight = data->image->pixelheight; if (palette == NULL) palette = data->image->palette; if (!(palette->type & f->req.supportedmask)) { #ifdef DEBUG printf ("Initalization of filter %s failed due to unsupported type by child %s-%i,%i\n", f->name, f->previous->name, f->req.supportedmask, palette->type); #endif f->image = data->image; return 0; } if (flags & NEWIMAGE) newimage = 1, sharedimage = 0, subimage = 0; if ((flags & IMAGEDATA) /*|| (data->image->flags & PROTECTBUFFERS) */ ) subimage = 0, sharedimage = 0, newimage = 1; /*if filter touches data but child requires them, create separated image */ if ((flags & TOUCHIMAGE) && ((f->req.flags & IMAGEDATA) || (data->image->flags & PROTECTBUFFERS))) subimage = 0, newimage = 1, sharedimage = 0; /*if required image differs in size or so */ if (width != data->image->width || height != data->image->height || palette != data->image->palette) newimage = 1, sharedimage = 0; if (f->childimage != NULL && (f->flags & ALLOCEDIMAGE)) { /*is an old child image still useable for us purposes? if not burn it it! */ /*We should share image? Why alloc new?? */ if (!newimage && (f->flags & ALLOCEDIMAGE)) destroyinheredimage(f), ddatalost = 1; /*We should share data? but child image dont do that! */ if (subimage && !(f->flags & SHAREDDATA)) destroyinheredimage(f), ddatalost = 1; /*We can't share data but child image does that? */ if (!subimage && (f->flags & SHAREDDATA)) destroyinheredimage(f), ddatalost = 1; /*When image changed, child image must be recreated too */ if (f->flags & SHAREDDATA && ((data->flags & DATALOST) || f->imageversion != data->image->version)) destroyinheredimage(f), ddatalost = 1; /*We should share image with filter? Why keep created new one? */ if (sharedimage) destroyinheredimage(f), ddatalost = 1; /*When child image don't fit out needs */ if (f->childimage != NULL && (f->childimage->width != width || f->childimage->height != height || f->childimage->palette != palette || f->childimage->bytesperpixel != bytesperpixel(palette->type) || f->childimage->nimages < f->req.nimages)) destroyinheredimage(f), ddatalost = 1; /*Well now child image seems to be heavily probed */ } i = f->childimage; if (newimage) { /*Create new image when required */ if (!(f->flags & ALLOCEDIMAGE)) { if (subimage) { i = create_subimage(data->image, width, height, f->req.nimages, palette, pixelwidth, pixelheight); f->flags |= ALLOCEDIMAGE | SHAREDDATA; ddatalost = 1; } else { i = create_image_mem(width, height, f->req.nimages, palette, pixelwidth, pixelheight); f->flags |= ALLOCEDIMAGE; ddatalost = 1; } } } #ifdef DEBUG printf("Filter:%s newimage:%i subimage:%i sharedimage:%i\n", f->name, newimage, subimage, sharedimage); #endif if (i == NULL) { f->image = data->image; return 0; } if (sharedimage) i = data->image, ddatalost = (data->flags & DATALOST) || (f->childimage != data->image); if (sharedimage && datalost(f, data)) ddatalost = 1; else if ((f->flags | SHAREDDATA) && datalost(f, data) && !(i->flags & FREEDATA)) ddatalost = 1; if (ddatalost) data->flags |= DATALOST; else data->flags &= ~DATALOST; f->image = data->image; f->childimage = i; f->imageversion = data->image->version; data->image = i; #ifdef DEBUG printf("OK %i datalost:%i\n", f->flags, ddatalost); #endif #ifdef DEBUG printf("Inherimage2:%s %i %i\n", f->name, width, height); #endif return 1; }
font_t* load_font(const char* path) { image_t* atlas = NULL; int atlas_size_x, atlas_size_y; ALLEGRO_LOCKED_REGION* bitmap_lock; FILE* file; font_t* font = NULL; struct font_glyph* glyph; struct rfn_glyph_header glyph_hdr; long glyph_start; int max_x = 0, max_y = 0; int min_width = INT_MAX; int64_t n_glyphs_per_row; size_t pixel_size; struct rfn_header rfn; uint8_t *src_ptr, *dest_ptr; int i, x, y; memset(&rfn, 0, sizeof(struct rfn_header)); if ((file = fopen(path, "rb")) == NULL) goto on_error; if (!(font = calloc(1, sizeof(font_t)))) goto on_error; if (fread(&rfn, sizeof(struct rfn_header), 1, file) != 1) goto on_error; pixel_size = (rfn.version == 1) ? 1 : 4; if (!(font->glyphs = calloc(rfn.num_chars, sizeof(struct font_glyph)))) goto on_error; // pass 1: load glyph headers and find largest glyph glyph_start = ftell(file); for (i = 0; i < rfn.num_chars; ++i) { glyph = &font->glyphs[i]; if (fread(&glyph_hdr, sizeof(struct rfn_glyph_header), 1, file) != 1) goto on_error; fseek(file, glyph_hdr.width * glyph_hdr.height * pixel_size, SEEK_CUR); max_x = fmax(glyph_hdr.width, max_x); max_y = fmax(glyph_hdr.height, max_y); min_width = fmin(min_width, glyph_hdr.width); glyph->width = glyph_hdr.width; glyph->height = glyph_hdr.height; } font->min_width = min_width; font->max_width = max_x; font->height = max_y; // create glyph atlas n_glyphs_per_row = ceil(sqrt(rfn.num_chars)); atlas_size_x = max_x * n_glyphs_per_row; atlas_size_y = max_y * n_glyphs_per_row; if ((atlas = create_image(atlas_size_x, atlas_size_y)) == NULL) goto on_error; // pass 2: load glyph data fseek(file, glyph_start, SEEK_SET); for (i = 0; i < rfn.num_chars; ++i) { glyph = &font->glyphs[i]; if (fread(&glyph_hdr, sizeof(struct rfn_glyph_header), 1, file) != 1) goto on_error; size_t data_size = glyph_hdr.width * glyph_hdr.height * pixel_size; void* data = malloc(data_size); if (fread(data, 1, data_size, file) != data_size) goto on_error; glyph->image = create_subimage(atlas, i % n_glyphs_per_row * max_x, i / n_glyphs_per_row * max_y, glyph_hdr.width, glyph_hdr.height); if (glyph->image == NULL) goto on_error; if ((bitmap_lock = al_lock_bitmap(get_image_bitmap(glyph->image), ALLEGRO_PIXEL_FORMAT_ABGR_8888, ALLEGRO_LOCK_WRITEONLY)) == NULL) goto on_error; src_ptr = data; dest_ptr = bitmap_lock->data; switch (rfn.version) { case 1: // RFN v1: 8-bit grayscale glyphs for (y = 0; y < glyph_hdr.height; ++y) { for (x = 0; x < glyph_hdr.width; ++x) { dest_ptr[x] = src_ptr[x]; dest_ptr[x + 1] = src_ptr[x]; dest_ptr[x + 2] = src_ptr[x]; dest_ptr[x + 3] = 255; dest_ptr += 4; } dest_ptr += bitmap_lock->pitch - (glyph_hdr.width * 4); src_ptr += glyph_hdr.width; } break; case 2: // RFN v2: 32-bit truecolor glyphs for (y = 0; y < glyph_hdr.height; ++y) { memcpy(dest_ptr, src_ptr, glyph_hdr.width * 4); dest_ptr += bitmap_lock->pitch; src_ptr += glyph_hdr.width * pixel_size; } break; } al_unlock_bitmap(get_image_bitmap(glyph->image)); free(data); } fclose(file); free_image(atlas); return ref_font(font); on_error: if (font != NULL) { for (i = 0; i < rfn.num_chars; ++i) { if (font->glyphs[i].image != NULL) free_image(font->glyphs[i].image); } free(font->glyphs); free(font); } if (atlas != NULL) free_image(atlas); return NULL; }