src/lib/cache.c
changeset 34 a387bc77ad52
parent 18 f92a24ab046e
child 52 148a120ea7d5
equal deleted inserted replaced
33:0ed40e11b0e8 34:a387bc77ad52
   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