491 |
491 |
492 // ok |
492 // ok |
493 return 0; |
493 return 0; |
494 } |
494 } |
495 |
495 |
|
496 static size_t scale_by_zoom_factor (size_t value, int z) |
|
497 { |
|
498 if (z > 0) |
|
499 return value << z; |
|
500 |
|
501 else if (z < 0) |
|
502 return value >> -z; |
|
503 |
|
504 else |
|
505 return value; |
|
506 } |
|
507 |
|
508 #define ADD_AVG(l, r) (l) = ((l) + (r)) / 2 |
|
509 |
|
510 static int png_pixel_data (png_color *out, struct pt_cache *cache, size_t row, size_t col) |
|
511 { |
|
512 if (cache->header->color_type == PNG_COLOR_TYPE_PALETTE) { |
|
513 // palette entry number |
|
514 int p; |
|
515 |
|
516 if (cache->header->bit_depth == 8) |
|
517 p = *((uint8_t *) tile_row_col(cache, row, col)); |
|
518 else |
|
519 return -1; |
|
520 |
|
521 if (p >= cache->header->num_palette) |
|
522 return -1; |
|
523 |
|
524 // reference data from palette |
|
525 *out = cache->header->palette[p]; |
|
526 |
|
527 return 0; |
|
528 |
|
529 } else { |
|
530 return -1; |
|
531 } |
|
532 } |
|
533 |
|
534 /** |
|
535 * Write unscaled tile data |
|
536 */ |
|
537 static int write_png_data_unzoomed (struct pt_cache *cache, png_structp png, png_infop info, const struct pt_tile_info *ti) |
|
538 { |
|
539 int err; |
|
540 |
|
541 // set basic info |
|
542 png_set_IHDR(png, info, ti->width, ti->height, cache->header->bit_depth, cache->header->color_type, |
|
543 PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT |
|
544 ); |
|
545 |
|
546 // set palette? |
|
547 if (cache->header->color_type == PNG_COLOR_TYPE_PALETTE) |
|
548 png_set_PLTE(png, info, cache->header->palette, cache->header->num_palette); |
|
549 |
|
550 // write meta-info |
|
551 png_write_info(png, info); |
|
552 |
|
553 // our pixel data is packed into 1 pixel per byte (8bpp or 16bpp) |
|
554 png_set_packing(png); |
|
555 |
|
556 // figure out if the tile clips |
|
557 if (ti->x + ti->width <= cache->header->width && ti->y + ti->height <= cache->header->height) |
|
558 // doesn't clip, just use the raw data |
|
559 err = write_png_data_direct(cache, png, info, ti); |
|
560 |
|
561 else |
|
562 // fill in clipped regions |
|
563 err = write_png_data_clipped(cache, png, info, ti); |
|
564 |
|
565 return err; |
|
566 } |
|
567 |
|
568 /** |
|
569 * Write scaled tile data |
|
570 */ |
|
571 static int write_png_data_zoomed (struct pt_cache *cache, png_structp png, png_infop info, const struct pt_tile_info *ti) |
|
572 { |
|
573 // size of the image data in px |
|
574 size_t data_width = scale_by_zoom_factor(ti->width, -ti->zoom); |
|
575 size_t data_height = scale_by_zoom_factor(ti->height, -ti->zoom); |
|
576 |
|
577 // input pixels per output pixel |
|
578 size_t pixel_size = scale_by_zoom_factor(1, -ti->zoom); |
|
579 |
|
580 // bytes per output pixel |
|
581 size_t pixel_bytes = 3; |
|
582 |
|
583 // size of the output tile in px |
|
584 size_t row_width = ti->width; |
|
585 |
|
586 // size of an output row in bytes (RGB) |
|
587 size_t row_bytes = row_width * 3; |
|
588 |
|
589 // buffer to hold output rows |
|
590 uint8_t *row_buf; |
|
591 |
|
592 // XXX: only supports zooming out... |
|
593 if (ti->zoom >= 0) |
|
594 RETURN_ERROR(PT_ERR_ZOOM); |
|
595 |
|
596 if ((row_buf = malloc(row_bytes)) == NULL) |
|
597 RETURN_ERROR(PT_ERR_MEM); |
|
598 |
|
599 |
|
600 // define pixel format: 8bpp RGB |
|
601 png_set_IHDR(png, info, ti->width, ti->height, 8, PNG_COLOR_TYPE_RGB, |
|
602 PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT |
|
603 ); |
|
604 |
|
605 // write meta-info |
|
606 png_write_info(png, info); |
|
607 |
|
608 // ...each output row |
|
609 for (size_t out_row = 0; out_row < ti->height; out_row++) { |
|
610 memset(row_buf, 0, row_bytes); |
|
611 |
|
612 // ...includes pixels starting from this row. |
|
613 size_t in_row_offset = ti->y + scale_by_zoom_factor(out_row, -ti->zoom); |
|
614 |
|
615 // ...each out row includes pixel_size in rows |
|
616 for (size_t in_row = in_row_offset; in_row < in_row_offset + pixel_size && in_row < cache->header->height; in_row++) { |
|
617 // and includes each input pixel |
|
618 for (size_t in_col = ti->x; in_col < ti->x + data_width && in_col < cache->header->width; in_col++) { |
|
619 png_color c; |
|
620 |
|
621 // ...for this output pixel |
|
622 size_t out_col = scale_by_zoom_factor(in_col - ti->x, ti->zoom); |
|
623 |
|
624 // get pixel RGB data |
|
625 if (png_pixel_data(&c, cache, in_row, in_col)) |
|
626 return -1; |
|
627 |
|
628 // average the RGB data |
|
629 ADD_AVG(row_buf[out_col * pixel_bytes + 0], c.red); |
|
630 ADD_AVG(row_buf[out_col * pixel_bytes + 1], c.green); |
|
631 ADD_AVG(row_buf[out_col * pixel_bytes + 2], c.blue); |
|
632 } |
|
633 } |
|
634 |
|
635 // output |
|
636 png_write_row(png, row_buf); |
|
637 } |
|
638 |
|
639 // done |
|
640 return 0; |
|
641 } |
|
642 |
496 int pt_cache_tile_png (struct pt_cache *cache, png_structp png, png_infop info, const struct pt_tile_info *ti) |
643 int pt_cache_tile_png (struct pt_cache *cache, png_structp png, png_infop info, const struct pt_tile_info *ti) |
497 { |
644 { |
498 int err; |
645 int err; |
499 |
646 |
500 // ensure open |
647 // ensure open |
503 |
650 |
504 // check within bounds |
651 // check within bounds |
505 if (ti->x >= cache->header->width || ti->y >= cache->header->height) |
652 if (ti->x >= cache->header->width || ti->y >= cache->header->height) |
506 // completely outside |
653 // completely outside |
507 RETURN_ERROR(PT_ERR_TILE_CLIP); |
654 RETURN_ERROR(PT_ERR_TILE_CLIP); |
508 |
655 |
509 // set basic info |
656 // unscaled or scaled? |
510 png_set_IHDR(png, info, ti->width, ti->height, cache->header->bit_depth, cache->header->color_type, |
657 if (ti->zoom) |
511 PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT |
658 err = write_png_data_zoomed(cache, png, info, ti); |
512 ); |
|
513 |
|
514 // set palette? |
|
515 if (cache->header->color_type == PNG_COLOR_TYPE_PALETTE) |
|
516 png_set_PLTE(png, info, cache->header->palette, cache->header->num_palette); |
|
517 |
|
518 // write meta-info |
|
519 png_write_info(png, info); |
|
520 |
|
521 // our pixel data is packed into 1 pixel per byte (8bpp or 16bpp) |
|
522 png_set_packing(png); |
|
523 |
|
524 // figure out if the tile clips |
|
525 if (ti->x + ti->width <= cache->header->width && ti->y + ti->height <= cache->header->height) |
|
526 // doesn't clip, just use the raw data |
|
527 err = write_png_data_direct(cache, png, info, ti); |
|
528 |
659 |
529 else |
660 else |
530 // fill in clipped regions |
661 err = write_png_data_unzoomed(cache, png, info, ti); |
531 err = write_png_data_clipped(cache, png, info, ti); |
|
532 |
662 |
533 if (err) |
663 if (err) |
534 return err; |
664 return err; |
535 |
665 |
536 // done, flush remaining output |
666 // done, flush remaining output |