272 { |
272 { |
273 return data + (row * header->row_bytes) + (col * header->col_bytes); |
273 return data + (row * header->row_bytes) + (col * header->col_bytes); |
274 } |
274 } |
275 |
275 |
276 /** |
276 /** |
277 * Fill in a clipped region of \a width_px pixels at the given row segment |
|
278 */ |
|
279 static inline void tile_row_fill_clip (const struct pt_png_header *header, png_byte *row, size_t width_px) |
|
280 { |
|
281 // XXX: use a configureable background color, or full transparency? |
|
282 memset(row, /* 0xd7 */ 0x00, width_px * header->col_bytes); |
|
283 } |
|
284 |
|
285 /** |
|
286 * Write raw tile image data, directly from the cache |
277 * Write raw tile image data, directly from the cache |
287 */ |
278 */ |
288 static int pt_png_encode_direct (struct pt_png_img *img, const struct pt_png_header *header, const uint8_t *data, const struct pt_tile_info *ti) |
279 static int pt_png_encode_direct (struct pt_png_img *img, const struct pt_png_header *header, const uint8_t *data, const struct pt_tile_info *ti) |
289 { |
280 { |
290 for (size_t row = ti->y; row < ti->y + ti->height; row++) |
281 for (size_t row = ti->y; row < ti->y + ti->height; row++) |
291 // write data directly |
282 // write data directly |
292 // missing const... |
283 // missing const... |
293 png_write_row(img->png, (const png_bytep) tile_row_col(header, data, row, ti->x)); |
284 png_write_row(img->png, (const png_bytep) tile_row_col(header, data, row, ti->x)); |
294 |
285 |
295 return 0; |
286 return 0; |
|
287 } |
|
288 |
|
289 /** |
|
290 * Fill in a clipped region of \a width_px pixels at the given row segment |
|
291 */ |
|
292 static inline void tile_row_fill_clip (const struct pt_png_header *header, png_byte *row, size_t width_px) |
|
293 { |
|
294 // XXX: use a configureable background color, or full transparency? |
|
295 memset(row, /* 0xd7 */ 0x00, width_px * header->col_bytes); |
296 } |
296 } |
297 |
297 |
298 /** |
298 /** |
299 * 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 |
299 * 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 |
300 */ |
300 */ |
343 |
343 |
344 // ok |
344 // ok |
345 return 0; |
345 return 0; |
346 } |
346 } |
347 |
347 |
348 static inline size_t scale_by_zoom_factor (size_t value, int z) |
|
349 { |
|
350 if (z > 0) |
|
351 return value << z; |
|
352 |
|
353 else if (z < 0) |
|
354 return value >> -z; |
|
355 |
|
356 else |
|
357 return value; |
|
358 } |
|
359 |
|
360 #define ADD_AVG(l, r) (l) = ((l) + (r)) / 2 |
|
361 |
|
362 static inline int png_pixel_data (png_color *out, const struct pt_png_header *header, const uint8_t *data, size_t row, size_t col) |
|
363 { |
|
364 if (header->color_type == PNG_COLOR_TYPE_PALETTE) { |
|
365 // palette entry number |
|
366 int p; |
|
367 |
|
368 if (header->bit_depth == 8) |
|
369 p = *((uint8_t *) tile_row_col(header, data, row, col)); |
|
370 else |
|
371 return -1; |
|
372 |
|
373 if (p >= header->num_palette) |
|
374 return -1; |
|
375 |
|
376 // reference data from palette |
|
377 *out = header->palette[p]; |
|
378 |
|
379 return 0; |
|
380 |
|
381 } else { |
|
382 return -1; |
|
383 } |
|
384 } |
|
385 |
|
386 /** |
348 /** |
387 * Write unscaled tile data |
349 * Write unscaled tile data |
388 */ |
350 */ |
389 static int pt_png_encode_unzoomed (struct pt_png_img *img, const struct pt_png_header *header, const uint8_t *data, const struct pt_tile_info *ti) |
351 static int pt_png_encode_unzoomed (struct pt_png_img *img, const struct pt_png_header *header, const uint8_t *data, const struct pt_tile_info *ti) |
390 { |
352 { |
417 |
379 |
418 return err; |
380 return err; |
419 } |
381 } |
420 |
382 |
421 /** |
383 /** |
|
384 * Manipulate powers of two |
|
385 */ |
|
386 static inline size_t scale_by_zoom_factor (size_t value, int z) |
|
387 { |
|
388 if (z > 0) |
|
389 return value << z; |
|
390 |
|
391 else if (z < 0) |
|
392 return value >> -z; |
|
393 |
|
394 else |
|
395 return value; |
|
396 } |
|
397 |
|
398 #define ADD_AVG(l, r) (l) = ((l) + (r)) / 2 |
|
399 |
|
400 /** |
|
401 * Converts a pixel's data into a png_color |
|
402 */ |
|
403 static inline void png_pixel_data (png_color *out, const struct pt_png_header *header, const uint8_t *data, size_t row, size_t col) |
|
404 { |
|
405 if (header->color_type == PNG_COLOR_TYPE_PALETTE) { |
|
406 // palette entry number |
|
407 int p; |
|
408 |
|
409 if (header->bit_depth == 8) |
|
410 p = *((uint8_t *) tile_row_col(header, data, row, col)); |
|
411 else |
|
412 // unknown |
|
413 return; |
|
414 |
|
415 if (p >= header->num_palette) |
|
416 return; |
|
417 |
|
418 // reference data from palette |
|
419 *out = header->palette[p]; |
|
420 |
|
421 } else { |
|
422 // unknown |
|
423 } |
|
424 } |
|
425 |
|
426 /** |
422 * Write scaled tile data |
427 * Write scaled tile data |
423 */ |
428 */ |
424 static int pt_png_encode_zoomed (struct pt_png_img *img, const struct pt_png_header *header, const uint8_t *data, const struct pt_tile_info *ti) |
429 static int pt_png_encode_zoomed (struct pt_png_img *img, const struct pt_png_header *header, const uint8_t *data, const struct pt_tile_info *ti) |
425 { |
430 { |
426 // size of the image data in px |
431 // size of the image data in px |
439 // size of an output row in bytes (RGB) |
444 // size of an output row in bytes (RGB) |
440 size_t row_bytes = row_width * 3; |
445 size_t row_bytes = row_width * 3; |
441 |
446 |
442 // buffer to hold output rows |
447 // buffer to hold output rows |
443 uint8_t *row_buf; |
448 uint8_t *row_buf; |
|
449 |
|
450 |
|
451 png_color c = { }; |
444 |
452 |
445 // only supports zooming out... |
453 // only supports zooming out... |
446 if (ti->zoom >= 0) |
454 if (ti->zoom >= 0) |
447 RETURN_ERROR(PT_ERR_TILE_ZOOM); |
455 RETURN_ERROR(PT_ERR_TILE_ZOOM); |
448 |
456 |
469 |
477 |
470 // ...each out row includes pixel_size in rows |
478 // ...each out row includes pixel_size in rows |
471 for (size_t in_row = in_row_offset; in_row < in_row_offset + pixel_size && in_row < header->height; in_row++) { |
479 for (size_t in_row = in_row_offset; in_row < in_row_offset + pixel_size && in_row < header->height; in_row++) { |
472 // and includes each input pixel |
480 // and includes each input pixel |
473 for (size_t in_col = ti->x; in_col < ti->x + data_width && in_col < header->width; in_col++) { |
481 for (size_t in_col = ti->x; in_col < ti->x + data_width && in_col < header->width; in_col++) { |
474 png_color c; |
|
475 |
482 |
476 // ...for this output pixel |
483 // ...for this output pixel |
477 size_t out_col = scale_by_zoom_factor(in_col - ti->x, ti->zoom); |
484 size_t out_col = scale_by_zoom_factor(in_col - ti->x, ti->zoom); |
478 |
485 |
479 // get pixel RGB data |
486 // get pixel RGB data |
480 if (png_pixel_data(&c, header, data, in_row, in_col)) |
487 png_pixel_data(&c, header, data, in_row, in_col); |
481 return -1; |
|
482 |
488 |
483 // average the RGB data |
489 // average the RGB data |
484 ADD_AVG(row_buf[out_col * pixel_bytes + 0], c.red); |
490 ADD_AVG(row_buf[out_col * pixel_bytes + 0], c.red); |
485 ADD_AVG(row_buf[out_col * pixel_bytes + 1], c.green); |
491 ADD_AVG(row_buf[out_col * pixel_bytes + 1], c.green); |
486 ADD_AVG(row_buf[out_col * pixel_bytes + 2], c.blue); |
492 ADD_AVG(row_buf[out_col * pixel_bytes + 2], c.blue); |