396 |
396 |
397 // done! |
397 // done! |
398 return 0; |
398 return 0; |
399 } |
399 } |
400 |
400 |
|
401 /** |
|
402 * Return a pointer to the pixel data on \a row, starting at \a col. |
|
403 */ |
|
404 static inline void* tile_row_col (struct pt_cache *cache, size_t row, size_t col) |
|
405 { |
|
406 return cache->data + (row * cache->header->row_bytes) + (col * cache->header->col_bytes); |
|
407 } |
|
408 |
|
409 /** |
|
410 * Fill in a clipped region of \a width_px pixels at the given row segment |
|
411 */ |
|
412 static inline void tile_row_fill_clip (struct pt_cache *cache, png_byte *row, size_t width_px) |
|
413 { |
|
414 // XXX: use a defined background color, or full transparency? |
|
415 memset(row, 0, width_px * cache->header->col_bytes); |
|
416 } |
|
417 |
|
418 /** |
|
419 * Write raw tile image data, directly from the cache |
|
420 */ |
|
421 static int write_png_data_direct (struct pt_cache *cache, png_structp png, png_infop info, const struct pt_tile_info *ti) |
|
422 { |
|
423 for (size_t row = ti->y; row < ti->y + ti->height; row++) |
|
424 // write data directly |
|
425 png_write_row(png, tile_row_col(cache, row, ti->x)); |
|
426 |
|
427 return 0; |
|
428 } |
|
429 |
|
430 /** |
|
431 * Write clipped tile image data (a tile that goes over the edge of the actual image) by aligning the data from the cache as needed |
|
432 */ |
|
433 static int write_png_data_clipped (struct pt_cache *cache, png_structp png, png_infop info, const struct pt_tile_info *ti) |
|
434 { |
|
435 png_byte *rowbuf; |
|
436 size_t row; |
|
437 |
|
438 // image data goes from (ti->x ... clip_x, ti->y ... clip_y), remaining region is filled |
|
439 size_t clip_x, clip_y; |
|
440 |
|
441 |
|
442 // figure out if the tile clips over the right edge |
|
443 // XXX: use min() |
|
444 if (ti->x + ti->width > cache->header->width) |
|
445 clip_x = cache->header->width; |
|
446 else |
|
447 clip_y = ti->x + ti->width; |
|
448 |
|
449 // figure out if the tile clips over the bottom edge |
|
450 // XXX: use min() |
|
451 if (ti->y + ti->height > cache->header->height) |
|
452 clip_y = cache->header->height; |
|
453 else |
|
454 clip_y = ti->y + ti->height; |
|
455 |
|
456 |
|
457 // allocate buffer for a single row of image data |
|
458 if ((rowbuf = malloc(ti->width * cache->header->col_bytes)) == NULL) |
|
459 return -1; |
|
460 |
|
461 // how much data we actually have for each row, in px and bytes |
|
462 // from [(tile x)---](clip x) |
|
463 size_t row_px = clip_x - ti->x; |
|
464 size_t row_bytes = row_px * cache->header->col_bytes; |
|
465 |
|
466 // write the rows that we have |
|
467 // from [(tile y]---](clip y) |
|
468 for (row = ti->y; row < clip_y; row++) { |
|
469 // copy in the actual tile data... |
|
470 memcpy(rowbuf, tile_row_col(cache, row, ti->x), row_bytes); |
|
471 |
|
472 // generate the data for the remaining, clipped, columns |
|
473 tile_row_fill_clip(cache, rowbuf + row_bytes, (ti->width - row_px)); |
|
474 |
|
475 // write |
|
476 png_write_row(png, rowbuf); |
|
477 } |
|
478 |
|
479 // generate the data for the remaining, clipped, rows |
|
480 tile_row_fill_clip(cache, rowbuf, ti->width); |
|
481 |
|
482 // write out the remaining rows as clipped data |
|
483 for (; row < ti->y + ti->height; row++) |
|
484 png_write_row(png, rowbuf); |
|
485 |
|
486 // ok |
|
487 return 0; |
|
488 } |
|
489 |
401 int pt_cache_tile_png (struct pt_cache *cache, png_structp png, png_infop info, const struct pt_tile_info *ti) |
490 int pt_cache_tile_png (struct pt_cache *cache, png_structp png, png_infop info, const struct pt_tile_info *ti) |
402 { |
491 { |
|
492 int err; |
|
493 |
403 // ensure open |
494 // ensure open |
404 if (pt_cache_open(cache)) |
495 if (pt_cache_open(cache)) |
405 return -1; |
496 return -1; |
406 |
497 |
407 // set basic info |
498 // set basic info |
414 png_set_PLTE(png, info, cache->header->palette, cache->header->num_palette); |
505 png_set_PLTE(png, info, cache->header->palette, cache->header->num_palette); |
415 |
506 |
416 // write meta-info |
507 // write meta-info |
417 png_write_info(png, info); |
508 png_write_info(png, info); |
418 |
509 |
419 // pixel data is packed into 1 pixel per byte |
510 // our pixel data is packed into 1 pixel per byte (8bpp or 16bpp) |
420 png_set_packing(png); |
511 png_set_packing(png); |
421 |
512 |
422 // write image data |
513 // figure out if the tile clips |
423 for (size_t row = ti->y; row < ti->y + ti->height; row++) { |
514 if (ti->x + ti->width <= cache->header->width && ti->y + ti->height <= cache->header->height) |
424 size_t col = ti->x; |
515 // doesn't clip, just use the raw data |
425 |
516 err = write_png_data_direct(cache, png, info, ti); |
426 // XXX: fill out-of-range regions in some background color |
517 |
427 png_write_row(png, cache->data + (row * cache->header->row_bytes) + (col * cache->header->col_bytes)); |
518 else |
428 } |
519 // fill in clipped regions |
429 |
520 err = write_png_data_clipped(cache, png, info, ti); |
|
521 |
|
522 if (err) |
|
523 return -1; |
|
524 |
430 // done, flush remaining output |
525 // done, flush remaining output |
431 png_write_flush(png); |
526 png_write_flush(png); |
432 |
527 |
433 // ok |
528 // ok |
434 return 0; |
529 return 0; |