#include "c4d_general.h" #include "c4d_memory.h" #include "c4d_gui.h" #include "x4d_filter.h" #include "c4d_bitmapfilter.h" #include "linefetchutil.h" struct ROW_BLOCK { ROW_BLOCK* next; BM_TILE* tile; Int32 xymin; // first line Int32 xymax; // last line Int32 ref; // usage counter Bool is_static; // true: do not detach this tile }; struct ROW_ROOT { BM_REF bm; Int32 x1; Int32 y1; Int32 x2; Int32 y2; Int32 tile_flags; Int32 tile_mode; BM_TILE* static_tile; Int32 update_flags; ROW_BLOCK* root; }; //---------------------------------------------------------------------------------------- // Create a new horizontal line fetcher object // Function result: line fetcher reference // bm: bitmap reference // x1, x2: minimum and maximum x coordinate (must be constant for all tiles in root) // tile_flags: TILE_REPEAT_BORDER, TILE_REPEAT_TILING, TILE_LAST_RESULT // tile_mode: TILE_BM_READ_ONLY, TILE_BM_WRITE, TILE_BM_READ_WRITE, TILE_BM_READ_SCRATCH // static_tile: use this parameter if all request are to be satisfied by this tile (and no other one) //---------------------------------------------------------------------------------------- ROW_REF new_row_fetcher(BM_REF bm, Int32 x1, Int32 x2, Int32 tile_flags, Int32 tile_mode, BM_TILE* static_tile, Int32 update_flags) { ROW_REF rr; rr = NewMemClear(ROW_ROOT, 1); if (rr) { rr->bm = bm; rr->tile_flags = tile_flags; rr->tile_mode = tile_mode; rr->x1 = x1; rr->y1 = 0; rr->x2 = x2; rr->y2 = 0; rr->static_tile = static_tile; rr->update_flags = update_flags; rr->root = 0; if (static_tile) { ROW_BLOCK* curr; curr = NewMemClear(ROW_BLOCK, 1); if (curr) { curr->next = 0; curr->tile = static_tile; // ###NOTE: no support for TILE_MODE_READ_SCRATCH curr->xymin = static_tile->ymin; curr->xymax = static_tile->ymax; curr->ref = 1; curr->is_static = true; // do not detach this tile! rr->root = curr; } else { DeleteMem(rr); rr = 0; } } } return rr; } //---------------------------------------------------------------------------------------- // Delete a line fetcher object // Function result: - // rr: line fetcher reference // discard_all: true: discard changes (is ignored for source tiles) //---------------------------------------------------------------------------------------- void delete_row_fetcher(ROW_REF rr, Bool discard_all) { if (rr) { while (rr->root) // are there any slices that have to be returned? { ROW_BLOCK* curr; curr = rr->root; rr->root = curr->next; if (curr->is_static == false) // can we detach this tile? { if ((rr->tile_mode & TILE_MODE_WRITE) && (discard_all == false)) { BfBitmapTileDetach(rr->bm, curr->tile, true); if (rr->update_flags & ROW_FETCH_UPDATE_VIEW) BfUpdateView(rr->bm); // update document view } else { BfBitmapTileDetach(rr->bm, curr->tile, false); } } DeleteMem(curr); // gosh! } DeleteMem(rr); } } //---------------------------------------------------------------------------------------- // Return the pointer to a requested horizontal line // Function result: pointer to the source line // rr: line fetcher reference // y: requested line //---------------------------------------------------------------------------------------- UChar* rf_get_bitmap_row(ROW_REF rr, Int32 y) { ROW_BLOCK** prev; prev = &rr->root; while (1) { ROW_BLOCK* curr; BM_TILE* tile; curr = *prev; if (curr) // is there a buffered tile? { tile = curr->tile; if ((y >= curr->xymin) && (y < curr->xymax)) { curr->ref++; return (UChar*) tile->addr + (tile->width * (y - curr->xymin)); } if ((curr->ref == 0) && (curr->is_static == false)) // is this tile not currently used and can we detach this tile? { *prev = curr->next; if (rr->tile_mode & TILE_MODE_WRITE) // return a destination tile? { BfBitmapTileDetach(rr->bm, tile, true); // destination tiles must (!!) be used in sequential order if (rr->update_flags & ROW_FETCH_UPDATE_VIEW) BfUpdateView(rr->bm); // update document view } else // it's a source tile { BfBitmapTileDetach(rr->bm, tile, false); // this assumes that source lines are processed in a more or less sequential order } DeleteMem(curr); continue; // check the next tile } } else // get a new tile { RECT32 rect; if (rr->static_tile) // are we not allowed to allocate a new tile? break; curr = NewMemClear(ROW_BLOCK, 1); if (curr) { Int32 mode; mode = rr->tile_mode; if (mode & TILE_MODE_READ_SCRATCH) { mode &= ~TILE_MODE_READ_SCRATCH; mode |= TILE_MODE_READ_WRITE; // handle this like a destination tile that will be thrown away } rect.x1 = rr->x1; rect.y1 = y; rect.x2 = rr->x2; rect.y2 = y + rr->bm->preferred_tile_height; // get preferred tile height tile = BfBitmapTileGet(rr->bm, &rect, 0, 0, mode, rr->tile_flags); if (tile) { curr->next = rr->root; curr->tile = tile; curr->xymin = rect.y1; curr->xymax = rect.y2; curr->ref = 1; curr->is_static = false; rr->root = curr; return (UChar*) curr->tile->addr + (curr->tile->width * (y - curr->xymin)); } else { DeleteMem(curr); return 0; } } else { break; } } prev = &curr->next; } return 0; } void rf_free_bitmap_row(ROW_REF rr, Int32 y) { ROW_BLOCK** prev; prev = &rr->root; while (*prev) { ROW_BLOCK* curr; curr = *prev; if ((y >= curr->xymin) && (y < curr->xymax)) { curr->ref--; if (curr->ref == 0) // do not free this tile immediately if ref is zero (wait for the next request) { *prev = curr->next; // unlink curr->next = rr->root; rr->root = curr; // make sure that this is the first tile that will be examined } break; // thank you very much } prev = &curr->next; } } //---------------------------------------------------------------------------------------- // Create a new vertical line fetcher object // Function result: line fetcher reference // bm: bitmap reference // y1, y2: minimum and maximum y coordinate (must be constant for all tiles in root) // tile_flags: TILE_REPEAT_BORDER, TILE_REPEAT_TILING, TILE_LAST_RESULT // tile_mode: TILE_BM_READ_ONLY, TILE_BM_WRITE, TILE_BM_READ_WRITE, TILE_BM_READ_SCRATCH // static_tile: use this parameter if all request are to be satisfied by this tile (and no other one) //---------------------------------------------------------------------------------------- ROW_REF new_column_fetcher(BM_REF bm, Int32 y1, Int32 y2, Int32 tile_flags, Int32 tile_mode, BM_TILE* static_tile, Int32 update_flags) { ROW_REF rr; rr = NewMemClear(ROW_ROOT, 1); if (rr) { rr->bm = bm; rr->tile_flags = tile_flags; rr->tile_mode = tile_mode; rr->x1 = 0; rr->y1 = y1; rr->x2 = 0; rr->y2 = y2; rr->static_tile = static_tile; rr->update_flags = update_flags; rr->root = 0; if (static_tile) { ROW_BLOCK* curr; curr = NewMemClear(ROW_BLOCK, 1); if (curr) { curr->next = 0; curr->tile = static_tile; // ###NOTE: no support for TILE_MODE_READ_SCRATCH curr->xymin = static_tile->xmin; curr->xymax = static_tile->xmax; curr->ref = 1; curr->is_static = true; // do not detach this tile! rr->root = curr; } else { DeleteMem(rr); rr = 0; } } } return rr; } //---------------------------------------------------------------------------------------- // Delete a vertical line fetcher object // Function result: - // rr: line fetcher reference // discard_all: true: discard changes (is ignored for source tiles) //---------------------------------------------------------------------------------------- void delete_column_fetcher(ROW_REF rr, Bool discard_all) { delete_row_fetcher(rr, discard_all); } //---------------------------------------------------------------------------------------- // Return the pointer to a requested vertical line // Function result: pointer to the source line // rr: line fetcher reference // x: requested line // pixel_offset: the distance to the next pixel is returned here (in bytes) //---------------------------------------------------------------------------------------- UChar* rf_get_bitmap_column(ROW_REF rr, Int32 x, Int32* pixel_offset) { ROW_BLOCK** prev; prev = &rr->root; while (1) { ROW_BLOCK* curr; BM_TILE* tile; curr = *prev; if (curr) // is there a buffered tile? { tile = curr->tile; if ((x >= curr->xymin) && (x < curr->xymax)) { curr->ref++; *pixel_offset = tile->width; return (UChar*) tile->addr + (((x - curr->xymin) * get_PX_BITS(tile->px_format)) >> 3); } if ((curr->ref == 0) && (curr->is_static == false)) // is this tile not currently used and can we detach this tile? { *prev = curr->next; if (rr->tile_mode & TILE_MODE_WRITE) // return a destination tile? { BfBitmapTileDetach(rr->bm, tile, true); // destination tiles must (!!) be used in sequential order if (rr->update_flags & ROW_FETCH_UPDATE_VIEW) BfUpdateView(rr->bm); // update document view } else // it's a source tile { BfBitmapTileDetach(rr->bm, tile, false); // this assumes that source lines are processed in a more or less sequential order } DeleteMem(curr); continue; // check the next tile } } else // get a new tile { RECT32 rect; if (rr->static_tile) // are we not allowed to allocate a new tile? break; curr = NewMemClear(ROW_BLOCK, 1); if (curr) { Int32 mode; mode = rr->tile_mode; if (mode & TILE_MODE_READ_SCRATCH) { mode &= ~TILE_MODE_READ_SCRATCH; mode |= TILE_MODE_READ_WRITE; // handle this like a destination tile that will be thrown away } rect.x1 = x; rect.y1 = rr->y1; rect.x2 = x + rr->bm->preferred_tile_width; // get preferred tile width rect.y2 = rr->y2; tile = BfBitmapTileGet(rr->bm, &rect, 0, 0, mode, rr->tile_flags); if (tile) { curr->next = rr->root; curr->tile = tile; curr->xymin = rect.x1; curr->xymax = rect.x2; curr->ref = 1; curr->is_static = false; rr->root = curr; *pixel_offset = tile->width; return (UChar*) tile->addr + (((x - curr->xymin) * get_PX_BITS(tile->px_format)) >> 3); } else { DeleteMem(curr); return 0; } } else { break; } } prev = &curr->next; } return 0; } void rf_free_bitmap_column(ROW_REF rr, Int32 x) { rf_free_bitmap_row(rr, x); } //---------------------------------------------------------------------------------------- // Convert from separated to premultiplied alpha // Function result: - // buf: line to be converted // width: # of pixels to copy // no_components: # of components per pixel // pixel_offset: offset from one pixel to the next in bytes //---------------------------------------------------------------------------------------- void premultiply_alpha(UChar* buf, Int32 width, Int32 no_components, Int32 pixel_offset) { pixel_offset -= no_components; no_components--; // decrement because it is used for the counter for (; width > 0; width--) { Int32 i; UInt32 alpha; alpha = *buf++; if (alpha == 255) { buf += no_components; // pixels won't change } else { for (i = no_components; i > 0; i--) { UInt32 v; v = *buf * alpha; v += 128; // round v = (v + (v >> 8) + 1) >> 8; // divide by 255 (v must be <= 65280) *buf++ = (UChar) v; } } buf += pixel_offset; // skip pixels (only vertical line) } } void premultiply_alpha(UInt16* buf, Int32 width, Int32 no_components, Int32 pixel_offset) { pixel_offset -= no_components; no_components--; // decrement because it is used for the counter for (; width > 0; width--) { Int32 i; UInt32 alpha; alpha = *buf++; if (alpha == 65535) { buf += no_components; // pixels won't change } else { for (i = no_components; i > 0; i--) { UInt32 v; v = *buf * alpha; v += 32768; // round v = (v + (v >> 16) + 1) >> 16; // divide by 255 (v must be <= 65280) *buf++ = (UInt16) v; } } buf += pixel_offset; // skip pixels (only vertical line) } } void premultiply_alpha(Float32* buf, Int32 width, Int32 no_components, Int32 pixel_offset) { pixel_offset -= no_components; no_components--; // decrement because it is used for the counter for (; width > 0; width--) { Int32 i; Float32 alpha; alpha = *buf++; if (alpha == 1.0) { buf += no_components; // pixels won't change } else { if (alpha < 0.0) { alpha = 0; buf[-1] = alpha; } else if (alpha > 1.0) { alpha = 1.0; buf[-1] = alpha; } for (i = no_components; i > 0; i--) { Float32 v; v = *buf * alpha; *buf++ = v; } } buf += pixel_offset; // skip pixels (only vertical line) } } //---------------------------------------------------------------------------------------- // Convert from premultiplied to separated alpha // Function result: - // buf: line to be converted // width: # of pixels to copy // no_components: # of components per pixel // pixel_offset: offset from one pixel to the next in bytes //---------------------------------------------------------------------------------------- void separate_alpha(UChar* buf, Int32 width, Int32 no_components, Int32 pixel_offset) { pixel_offset -= no_components; no_components--; // decrement because it is used for the counter for (; width > 0; width--) { UInt32 alpha; alpha = *buf++; if ((alpha == 0) || (alpha == 255)) { buf += no_components; // no need to change the pixel values } else { Int32 i; UInt32 recip_alpha; recip_alpha = (255L << 16) / alpha; for (i = no_components; i > 0; i--) { UInt32 v; v = ((*buf * recip_alpha) + 32768L) >> 16; // v * ( 255.0 / (Float) alpha ) if (v > 255) v = 255; // premultiplied values can exceed the normal range *buf++ = v; } } buf += pixel_offset; // skip pixels (only vertical line) } } void separate_alpha(UInt16* buf, Int32 width, Int32 no_components, Int32 pixel_offset) { pixel_offset -= no_components; no_components--; // decrement because it is used for the counter for (; width > 0; width--) { UInt32 alpha; alpha = *buf++; if ((alpha == 0) || (alpha == 65535)) { buf += no_components; // no need to change the pixel values } else { Int32 i; for (i = no_components; i > 0; i--) { UInt32 v; v = ((*buf) << 16) / alpha; // recip_alpha ) + 32768L ) >> 16; // v * ( 255.0 / (Float) alpha ) if (v > 65535) v = 65535; // premultiplied values can exceed the normal range *buf++ = v; } } buf += pixel_offset; // skip pixels (only vertical line) } } void separate_alpha(Float32* buf, Int32 width, Int32 no_components, Int32 pixel_offset, Float32 clip) { pixel_offset -= no_components; no_components--; // decrement because it is used for the counter for (; width > 0; width--) { Float32 alpha; alpha = *buf++; if (alpha < clip) { alpha = 0.0; buf[-1] = alpha; } else if (alpha > 1.0) { alpha = 1.0; buf[-1] = alpha; } if ((alpha == 0.0) || (alpha == 1.0)) { buf += no_components; // no need to change the pixel values } else { Int32 i; Float64 recip_alpha; recip_alpha = 1.0 / alpha; for (i = no_components; i > 0; i--) { Float32 v; v = Float64(*buf) * recip_alpha; *buf++ = v; } } buf += pixel_offset; // skip pixels (only vertical line) } }