// Must be threadsafe void process_op(uint16_t *rgba_p, uint16_t *mask, int tx, int ty, OperationDataDrawDab *op) { // first, we calculate the mask (opacity for each pixel) render_dab_mask(mask, op->x - tx*MYPAINT_TILE_SIZE, op->y - ty*MYPAINT_TILE_SIZE, op->radius, op->hardness, op->aspect_ratio, op->angle ); // second, we use the mask to stamp a dab for each activated blend mode if (op->normal) { if (op->color_a == 1.0) { draw_dab_pixels_BlendMode_Normal(mask, rgba_p, op->color_r, op->color_g, op->color_b, op->normal*op->opaque*(1<<15)); } else { // normal case for brushes that use smudging (eg. watercolor) draw_dab_pixels_BlendMode_Normal_and_Eraser(mask, rgba_p, op->color_r, op->color_g, op->color_b, op->color_a*(1<<15), op->normal*op->opaque*(1<<15)); } } if (op->lock_alpha) { draw_dab_pixels_BlendMode_LockAlpha(mask, rgba_p, op->color_r, op->color_g, op->color_b, op->lock_alpha*op->opaque*(1<<15)); } if (op->colorize) { draw_dab_pixels_BlendMode_Color(mask, rgba_p, op->color_r, op->color_g, op->color_b, op->colorize*op->opaque*(1<<15)); } }
void get_color (MyPaintSurface *surface, float x, float y, float radius, float * color_r, float * color_g, float * color_b, float * color_a ) { MyPaintTiledSurface *self = (MyPaintTiledSurface *)surface; if (radius < 1.0f) radius = 1.0f; const float hardness = 0.5f; const float aspect_ratio = 1.0f; const float angle = 0.0f; float sum_weight, sum_r, sum_g, sum_b, sum_a; sum_weight = sum_r = sum_g = sum_b = sum_a = 0.0f; // in case we return with an error *color_r = 0.0f; *color_g = 1.0f; *color_b = 0.0f; // WARNING: some code duplication with draw_dab float r_fringe = radius + 1.0f; // +1 should not be required, only to be sure int tx1 = floor(floor(x - r_fringe) / MYPAINT_TILE_SIZE); int tx2 = floor(floor(x + r_fringe) / MYPAINT_TILE_SIZE); int ty1 = floor(floor(y - r_fringe) / MYPAINT_TILE_SIZE); int ty2 = floor(floor(y + r_fringe) / MYPAINT_TILE_SIZE); #ifdef _OPENMP int tiles_n = (tx2 - tx1) * (ty2 - ty1); #endif #pragma omp parallel for schedule(static) if(self->threadsafe_tile_requests && tiles_n > 3) for (int ty = ty1; ty <= ty2; ty++) { for (int tx = tx1; tx <= tx2; tx++) { // Flush queued draw_dab operations process_tile(self, tx, ty); MyPaintTileRequest request_data; const int mipmap_level = 0; mypaint_tile_request_init(&request_data, mipmap_level, tx, ty, TRUE); mypaint_tiled_surface_tile_request_start(self, &request_data); uint16_t * rgba_p = request_data.buffer; if (!rgba_p) { printf("Warning: Unable to get tile!\n"); break; } // first, we calculate the mask (opacity for each pixel) uint16_t mask[MYPAINT_TILE_SIZE*MYPAINT_TILE_SIZE+2*MYPAINT_TILE_SIZE]; render_dab_mask(mask, x - tx*MYPAINT_TILE_SIZE, y - ty*MYPAINT_TILE_SIZE, radius, hardness, aspect_ratio, angle ); // TODO: try atomic operations instead #pragma omp critical { get_color_pixels_accumulate (mask, rgba_p, &sum_weight, &sum_r, &sum_g, &sum_b, &sum_a); } mypaint_tiled_surface_tile_request_end(self, &request_data); } } assert(sum_weight > 0.0f); sum_a /= sum_weight; sum_r /= sum_weight; sum_g /= sum_weight; sum_b /= sum_weight; *color_a = sum_a; // now un-premultiply the alpha if (sum_a > 0.0f) { *color_r = sum_r / sum_a; *color_g = sum_g / sum_a; *color_b = sum_b / sum_a; } else { // it is all transparent, so don't care about the colors // (let's make them ugly so bugs will be visible) *color_r = 0.0f; *color_g = 1.0f; *color_b = 0.0f; } // fix rounding problems that do happen due to floating point math *color_r = CLAMP(*color_r, 0.0f, 1.0f); *color_g = CLAMP(*color_g, 0.0f, 1.0f); *color_b = CLAMP(*color_b, 0.0f, 1.0f); *color_a = CLAMP(*color_a, 0.0f, 1.0f); }
void get_color (float x, float y, float radius, float * color_r, float * color_g, float * color_b, float * color_a ) { float r_fringe; if (radius < 1.0) radius = 1.0; const float hardness = 0.5; const float aspect_ratio = 1.0; const float angle = 0.0; float sum_weight, sum_r, sum_g, sum_b, sum_a; sum_weight = sum_r = sum_g = sum_b = sum_a = 0.0; // in case we return with an error *color_r = 0.0; *color_g = 1.0; *color_b = 0.0; // WARNING: some code duplication with draw_dab r_fringe = radius + 1; int tx1 = floor(floor(x - r_fringe) / TILE_SIZE); int tx2 = floor(floor(x + r_fringe) / TILE_SIZE); int ty1 = floor(floor(y - r_fringe) / TILE_SIZE); int ty2 = floor(floor(y + r_fringe) / TILE_SIZE); int tx, ty; for (ty = ty1; ty <= ty2; ty++) { for (tx = tx1; tx <= tx2; tx++) { uint16_t * rgba_p = get_tile_memory(tx, ty, true); if (!rgba_p) { printf("Python exception during get_color()!\n"); return; } // first, we calculate the mask (opacity for each pixel) static uint16_t mask[TILE_SIZE*TILE_SIZE+2*TILE_SIZE]; render_dab_mask(mask, x - tx*TILE_SIZE, y - ty*TILE_SIZE, radius, hardness, aspect_ratio, angle ); get_color_pixels_accumulate (mask, rgba_p, &sum_weight, &sum_r, &sum_g, &sum_b, &sum_a); } } assert(sum_weight > 0.0); sum_a /= sum_weight; sum_r /= sum_weight; sum_g /= sum_weight; sum_b /= sum_weight; *color_a = sum_a; // now un-premultiply the alpha if (sum_a > 0.0) { *color_r = sum_r / sum_a; *color_g = sum_g / sum_a; *color_b = sum_b / sum_a; } else { // it is all transparent, so don't care about the colors // (let's make them ugly so bugs will be visible) *color_r = 0.0; *color_g = 1.0; *color_b = 0.0; } // fix rounding problems that do happen due to floating point math *color_r = CLAMP(*color_r, 0.0, 1.0); *color_g = CLAMP(*color_g, 0.0, 1.0); *color_b = CLAMP(*color_b, 0.0, 1.0); *color_a = CLAMP(*color_a, 0.0, 1.0); }
// returns true if the surface was modified bool draw_dab (float x, float y, float radius, float color_r, float color_g, float color_b, float opaque, float hardness = 0.5, float color_a = 1.0, float aspect_ratio = 1.0, float angle = 0.0, float lock_alpha = 0.0 ) { opaque = CLAMP(opaque, 0.0, 1.0); hardness = CLAMP(hardness, 0.0, 1.0); lock_alpha = CLAMP(lock_alpha, 0.0, 1.0); if (radius < 0.1) return false; // don't bother with dabs smaller than 0.1 pixel if (hardness == 0.0) return false; // infintly small center point, fully transparent outside if (opaque == 0.0) return false; assert(atomic > 0); color_r = CLAMP(color_r, 0.0, 1.0); color_g = CLAMP(color_g, 0.0, 1.0); color_b = CLAMP(color_b, 0.0, 1.0); color_a = CLAMP(color_a, 0.0, 1.0); uint16_t color_r_ = color_r * (1<<15); uint16_t color_g_ = color_g * (1<<15); uint16_t color_b_ = color_b * (1<<15); // blending mode preparation float normal = 1.0; normal *= 1.0-lock_alpha; if (aspect_ratio<1.0) aspect_ratio=1.0; float r_fringe = radius + 1; int tx1 = floor(floor(x - r_fringe) / TILE_SIZE); int tx2 = floor(floor(x + r_fringe) / TILE_SIZE); int ty1 = floor(floor(y - r_fringe) / TILE_SIZE); int ty2 = floor(floor(y + r_fringe) / TILE_SIZE); int tx, ty; for (ty = ty1; ty <= ty2; ty++) { for (tx = tx1; tx <= tx2; tx++) { uint16_t * rgba_p = get_tile_memory(tx, ty, false); if (!rgba_p) { printf("Python exception during draw_dab()!\n"); return true; } // first, we calculate the mask (opacity for each pixel) static uint16_t mask[TILE_SIZE*TILE_SIZE+2*TILE_SIZE]; render_dab_mask(mask, x - tx*TILE_SIZE, y - ty*TILE_SIZE, radius, hardness, aspect_ratio, angle ); // second, we use the mask to stamp a dab for each activated blend mode if (normal) { if (color_a == 1.0) { draw_dab_pixels_BlendMode_Normal(mask, rgba_p, color_r_, color_g_, color_b_, normal*opaque*(1<<15)); } else { // normal case for brushes that use smudging (eg. watercolor) draw_dab_pixels_BlendMode_Normal_and_Eraser(mask, rgba_p, color_r_, color_g_, color_b_, color_a*(1<<15), normal*opaque*(1<<15)); } } if (lock_alpha) { draw_dab_pixels_BlendMode_LockAlpha(mask, rgba_p, color_r_, color_g_, color_b_, lock_alpha*opaque*(1<<15)); } } } { // expand the bounding box to include the region we just drawed int bb_x, bb_y, bb_w, bb_h; bb_x = floor (x - (radius+1)); bb_y = floor (y - (radius+1)); /* FIXME: think about it exactly */ bb_w = ceil (2*(radius+1)); bb_h = ceil (2*(radius+1)); ExpandRectToIncludePoint (&dirty_bbox, bb_x, bb_y); ExpandRectToIncludePoint (&dirty_bbox, bb_x+bb_w-1, bb_y+bb_h-1); } return true; }