gint clip_x, gint clip_y, gint clip_w, gint clip_h,
gint *rx, gint *ry, gint *rw, gint *rh)
{
- if (clip_x + clip_w <= x ||
- clip_x >= x + w ||
- clip_y + clip_h <= y ||
- clip_y >= y + h)
+ // Ensures that clip region and main region have some overlap (they aren't
+ // completely disjoint).
+ if (clip_x + clip_w <= x || /* assert(x < clip_right) && */
+ clip_x >= x + w || /* assert(clip_x < right) && */
+ clip_y + clip_h <= y || /* assert(y < clip_bottom && */
+ clip_y >= y + h) /* assert(bottom < clip_y) */
{
return FALSE;
}
+ // We choose the right-most x coordinate.
*rx = MAX(x, clip_x);
+ // And the narrowest width.
*rw = MIN((x + w), (clip_x + clip_w)) - *rx;
+ // We choose the bottom-most y coordinate.
*ry = MAX(y, clip_y);
+ // And the shortest height.
*rh = MIN((y + h), (clip_y + clip_h)) - *ry;
return TRUE;
pp[0] = (r * a + pp[0] * (256-a)) >> 8;
pp[1] = (g * a + pp[1] * (256-a)) >> 8;
pp[2] = (b * a + pp[2] * (256-a)) >> 8;
+ // TODO(xsdg): Should we do anything about a potential
+ // existing alpha value here?
pp += p_step;
}
}
void pixbuf_set_rect(GdkPixbuf *pb,
gint x, gint y, gint w, gint h,
gint r, gint g, gint b, gint a,
- gint left, gint right, gint top, gint bottom)
+ gint left_width, gint right_width, gint top_width, gint bottom_width)
{
- pixbuf_set_rect_fill(pb, x + left, y, w - left - right, top,
+ // TODO(xsdg): This function has multiple off-by-one errors. Would be
+ // much easier to read (and implement correctly) with temporaries to
+ // translate from (x, y, w, h) coordinates to (x1, y1, x2, y2).
+ pixbuf_set_rect_fill(pb,
+ x + left_width, y, w - left_width - right_width, top_width,
r, g, b ,a);
- pixbuf_set_rect_fill(pb, x + w - right, y, right, h,
+ pixbuf_set_rect_fill(pb,
+ x + w - right_width, y, right_width, h,
r, g, b ,a);
- pixbuf_set_rect_fill(pb, x + left, y + h - bottom, w - left - right, bottom,
+ pixbuf_set_rect_fill(pb,
+ x + left_width, y + h - bottom_width, w - left_width - right_width, bottom_width,
r, g, b ,a);
- pixbuf_set_rect_fill(pb, x, y, left, h,
+ pixbuf_set_rect_fill(pb,
+ x, y, left_width, h,
r, g, b ,a);
}
pw = gdk_pixbuf_get_width(pb);
ph = gdk_pixbuf_get_height(pb);
+ // Intersects the clip region with the pixbuf. r{x,y,w,h} is that
+ // intersecting region.
if (!util_clip_region(0, 0, pw, ph,
clip_x, clip_y, clip_w, clip_h,
&rx, &ry, &rw, &rh)) return;
+ // Determine the bounding box for the triangle.
util_clip_triangle(x1, y1, x2, y2, x3, y3,
tx, ty, tw, th);
+ // And now clip the triangle bounding box to the pixbuf clipping region.
if (!util_clip_region(rx, ry, rw, rh,
tx, ty, tw, th,
&fx1, &fy1, &fw, &fh)) return;
p_step = (has_alpha) ? 4 : 3;
+ // Ensure that points are ordered by increasing y coordinate.
if (y1 > y2)
{
std::swap(x1, x2);
std::swap(y1, y2);
}
+ // TODO(xsdg): Drop these explicit casts. Int will always promote to
+ // double without issue.
slope1 = static_cast<gdouble>(y2 - y1);
if (slope1) slope1 = static_cast<gdouble>(x2 - x1) / slope1;
slope1_x = x1;
*-----------------------------------------------------------------------------
*/
+/**
+ * @brief Clips the specified line segment to the specified clipping region.
+ * @param[in] clip_x,clip_y Coordinates of the top-left corner of the clipping region.
+ * @param[in] clip_w,clip_h Extent of the clipping region.
+ * @param[in] x1,y1 Coordinates of the first point of the line segment.
+ * @param[in] x2,y2 Coordinates of the second point of the line segment.
+ * @param[out] rx1,ry1 Computed coordinates of the first point of the clipped line segment.
+ * @param[out] rx2,ry2 Computed coordinates of the second point of the clipped line segment.
+ * @retval FALSE The line segment lies outside of the clipping region.
+ * @retval TRUE The clip operation was performed, and the output params were set.
+ */
static gboolean util_clip_line(gdouble clip_x, gdouble clip_y, gdouble clip_w, gdouble clip_h,
gdouble x1, gdouble y1, gdouble x2, gdouble y2,
gdouble *rx1, gdouble *ry1, gdouble *rx2, gdouble *ry2)
gboolean flip = FALSE;
gdouble d;
+ // Normalize: Line endpoint 1 must be farther left.
if (x1 > x2)
{
std::swap(x1, x2);
flip = TRUE;
}
+ // Ensure the line horizontally overlaps with the clip region.
if (x2 < clip_x || x1 > clip_x + clip_w) return FALSE;
+ // Ensure the line vertically overlaps with the clip region.
+ // Note that a line can both horizontally and vertically overlap with
+ // clipping region, while still being outside of the clipping region. That
+ // case is detected further below.
if (y1 < y2)
{
if (y2 < clip_y || y1 > clip_y + clip_h) return FALSE;
}
d = x2 - x1;
+ // TODO(xsdg): Either use ints here, or define a reasonable epsilon to do the
+ // right thing if -epsilon < d < 0. We already guaranteed above that x2 >= x1.
if (d > 0.0)
{
gdouble slope;
slope = (y2 - y1) / d;
+ // If needed, project (x1, y1) to be horizontally within the clip
+ // region, while maintaining the line's slope and y-offset.
if (x1 < clip_x)
{
y1 = y1 + slope * (clip_x - x1);
x1 = clip_x;
}
+ // Likewise with (x2, y2).
if (x2 > clip_x + clip_w)
{
y2 = y2 + slope * (clip_x + clip_w - x2);
}
}
+ // Check that any horizontal projections didn't cause the line segment to
+ // no longer vertically overlap with the clip region.
if (y1 < y2)
{
if (y2 < clip_y || y1 > clip_y + clip_h) return FALSE;
{
if (y1 < clip_y || y2 > clip_y + clip_h) return FALSE;
+ // Re-normalize: line endpoint 1 must be farther up.
std::swap(x1, x2);
std::swap(y1, y2);
flip = !flip;
gdouble slope;
slope = (x2 - x1) / d;
+ // If needed, project (x1, y1) to be vertically within the clip
+ // region, while maintaining the line's slope and x-offset.
if (y1 < clip_y)
{
x1 = x1 + slope * (clip_y - y1);
y1 = clip_y;
}
+ // Likewise with (x2, y2).
if (y2 > clip_y + clip_h)
{
x2 = x2 + slope * (clip_y + clip_h - y2);
}
}
+ // Set the output params, accounting for any flips that might have
+ // happened during normalization.
if (flip)
{
*rx1 = x2;
pw = gdk_pixbuf_get_width(pb);
ph = gdk_pixbuf_get_height(pb);
+ // Intersects the clip region with the pixbuf. r{x,y,w,h} is that
+ // intersecting region.
if (!util_clip_region(0, 0, pw, ph,
clip_x, clip_y, clip_w, clip_h,
&rx, &ry, &rw, &rh)) return;
+ // TODO(xsdg): These explicit casts are unnecessary and harm readability.
+ // Clips the specified line segment to the intersecting region from above.
if (!util_clip_line(static_cast<gdouble>(rx), static_cast<gdouble>(ry), static_cast<gdouble>(rw), static_cast<gdouble>(rh),
static_cast<gdouble>(x1), static_cast<gdouble>(y1), static_cast<gdouble>(x2), static_cast<gdouble>(y2),
&rx1, &ry1, &rx2, &ry2)) return;
p_step = (has_alpha) ? 4 : 3;
+ // We draw the clipped line segment along the longer axis first, and
+ // allow the shorter axis to follow. This is because our raster line segment
+ // will contain max(rx2-rx1, ry2-ry1) pixels, and the pixels along the
+ // shorter axis may not advance for each cycle (the line is not anti-aliased).
if (fabs(rx2 - rx1) > fabs(ry2 - ry1))
{
if (rx1 > rx2)
*-----------------------------------------------------------------------------
*/
+/**
+ * @brief Composites a horizontal or vertical linear gradient into the rectangular
+ * region defined by corners `(x1, y1)` and `(x2, y2)`. Note that the
+ * current implementation breaks if the max distance between `s` and
+ * `x1/x2/y1/y2` is greater than `border`.
+ * @param p_pix The pixel buffer to paint into.
+ * @param prs The pixel row stride (how many pixels per row of the buffer).
+ * @param has_alpha TRUE if the p_pix representation is rgba. FALSE if just rgb.
+ * @param s The "center" of the gradient, along the axis defined by `vertical`.
+ * Note that if the center is not along an edge, the gradient will be
+ * symmetric about the center.
+ * @param vertical When `TRUE`, the gradient color will vary vertically. When `FALSE`,
+ * horizontally.
+ * @param border The maximum extent of the gradient, in pixels.
+ * @param x1,y1 Coordinates of the first corner of the region.
+ * @param x2,y2 Coordinates of the second corner of the region.
+ * @param r,g,b Base color of the gradient.
+ * @param a The peak alpha value when compositing the gradient. The alpha varies
+ * from this value down to 0 (fully transparent). Note that any alpha
+ * value associated with the original pixel is unmodified.
+ */
static void pixbuf_draw_fade_linear(guchar *p_pix, gint prs, gboolean has_alpha,
gint s, gboolean vertical, gint border,
gint x1, gint y1, gint x2, gint y2,
}
}
+/**
+ * @brief Composites a radial gradient into the rectangular region defined by
+ * corners `(x1, y1)` and `(x2, y2)`.
+ * @param p_pix The pixel buffer to paint into.
+ * @param prs The pixel row stride (how many pixels per row of the buffer).
+ * @param has_alpha TRUE if the p_pix representation is rgba. FALSE if just rgb.
+ * @param sx,sy The coordinates of the center of the gradient.
+ * @param border The max radius, in pixels, of the gradient. Pixels farther away
+ * from the center than this will be unaffected.
+ * @param x1,y1 Coordinates of the first corner of the region.
+ * @param x2,y2 Coordinates of the second corner of the region.
+ * @param r,g,b Base color of the gradient.
+ * @param a The peak alpha value when compositing the gradient. The alpha varies
+ * from this value down to 0 (fully transparent). Note that any alpha
+ * value associated with the original pixel is unmodified.
+ */
static void pixbuf_draw_fade_radius(guchar *p_pix, gint prs, gboolean has_alpha,
gint sx, gint sy, gint border,
gint x1, gint y1, gint x2, gint y2,
guint8, guint8 g, guint8 b, guint8 a)
{
+ // TODO(xsdg): r (red) was shadowed by r (radius), and was removed from
+ // the params list by an automated cleanup. Fix this and distinguish the
+ // red param from the radius temporary variable.
guchar *pp;
gint p_step;
gint i;
pw = gdk_pixbuf_get_width(pb);
ph = gdk_pixbuf_get_height(pb);
+ // Intersects the clip region with the pixbuf. r{x,y,w,h} is that
+ // intersecting region.
if (!util_clip_region(0, 0, pw, ph,
clip_x, clip_y, clip_w, clip_h,
&rx, &ry, &rw, &rh)) return;
prs = gdk_pixbuf_get_rowstride(pb);
p_pix = gdk_pixbuf_get_pixels(pb);
+ // Composites the specified color into the rectangle specified by x, y, w, h,
+ // as contracted by `border` pixels, with a composition fraction that's defined
+ // by the supplied `a` parameter.
if (util_clip_region(x + border, y + border, w - border * 2, h - border * 2,
rx, ry, rw, rh,
&fx, &fy, &fw, &fh))
if (border < 1) return;
+ // Draws linear gradients along each of the 4 edges.
if (util_clip_region(x, y + border, border, h - border * 2,
rx, ry, rw, rh,
&fx, &fy, &fw, &fh))
fx, fy, fx + fw, fy + fh,
r, g, b, a);
}
+ // Draws radial gradients at each of the 4 corners.
if (util_clip_region(x, y, border, border,
rx, ry, rw, rh,
&fx, &fy, &fw, &fh))
/*
*-----------------------------------------------------------------------------
- * pixbuf highlight under/over exposure *-----------------------------------------------------------------------------
+ * pixbuf highlight under/over exposure
+ *-----------------------------------------------------------------------------
*/
void pixbuf_highlight_overunderexposed(GdkPixbuf *pb, gint x, gint y, gint w, gint h)
{
*-----------------------------------------------------------------------------
*/
void pixbuf_ignore_alpha_rect(GdkPixbuf *pb,
- gint x, gint y, gint w, gint h)
+ gint x, gint y, gint w, gint h)
{
gboolean has_alpha;
gint pw;
GdkPixbuf *pixbuf_copy_mirror(GdkPixbuf *src, gboolean mirror, gboolean flip);
GdkPixbuf* pixbuf_apply_orientation(GdkPixbuf *pixbuf, gint orientation);
+/**
+ * @brief Composites the fill color with the existing contents of the pixbuf,
+ * within the specified region, with a proportion set by the alpha (`a`)
+ * parameter.
+ * @param pb The `GdkPixbuf` to paint into.
+ * @param x,y Coordinates of the top-left corner of the first region.
+ * @param w,h Extent of the first region.
+ * @param r,g,b Fill color.
+ * @param a The alpha to use for compositing. a=255 is solid (fully the new
+ * color). a=0 is tranparent (fully the original contents).
+ */
void pixbuf_draw_rect_fill(GdkPixbuf *pb,
gint x, gint y, gint w, gint h,
gint r, gint g, gint b, gint a);
+/**
+ * @brief Fills the specified region of the pixbuf with the specified color.
+ * @param pb The `GdkPixbuf` to paint into.
+ * @param x,y Coordinates of the top-left corner of the first region.
+ * @param w,h Extent of the first region.
+ * @param r,g,b,a Fill color and alpha.
+ */
void pixbuf_set_rect_fill(GdkPixbuf *pb,
gint x, gint y, gint w, gint h,
gint r, gint g, gint b, gint a);
+/**
+ * @brief Draws a rectangular stroke of the specified stroke width and color
+ * around the specified region of the pixbuf.
+ * @param pb The `GdkPixbuf` to paint into.
+ * @param x,y Coordinates of the top-left corner of the region.
+ * @param w,h Extent of the region.
+ * @param r,g,b,a Line color and alpha.
+ * @param left_width Stroke width of the left edge of the rectangle.
+ * @param right_width Stroke width of the right edge of the rectangle.
+ * @param top_width Stroke width of the top edge of the rectangle.
+ * @param bottom_width Stroke width of the bottom edge of the rectangle.
+ */
void pixbuf_set_rect(GdkPixbuf *pb,
gint x, gint y, gint w, gint h,
gint r, gint g, gint b, gint a,
- gint left, gint right, gint top, gint bottom);
+ gint left_width, gint right_width, gint top_width, gint bottom_width);
+/**
+ * @brief Sets the specified pixel of the pixbuf to the specified color.
+ * @param pb The `GdkPixbuf` to paint into.
+ * @param x,y Coordinates of the pixel to set.
+ * @param r,g,b,a Color and alpha.
+ */
void pixbuf_pixel_set(GdkPixbuf *pb, gint x, gint y, gint r, gint g, gint b, gint a);
guint8 r, guint8 g, guint8 b, guint8 a);
+/**
+ * @brief Draws a filled triangle of the specified color into the pixbuf, constrained
+ * to the specified clip region.
+ * @param pb The `GdkPixbuf` to paint into.
+ * @param x1,y1 Coordinates of the first corner of the triangle.
+ * @param x2,y2 Coordinates of the second corner of the triangle.
+ * @param x3,y3 Coordinates of the third corner of the triangle.
+ * @param clip_x,clip_y Coordinates of the top-left corner of the clipping region.
+ * @param clip_w,clip_h Extent of the clipping region.
+ * @param r,g,b,a Color and alpha.
+ */
void pixbuf_draw_triangle(GdkPixbuf *pb,
gint clip_x, gint clip_y, gint clip_w, gint clip_h,
gint x1, gint y1, gint x2, gint y2, gint x3, gint y3,
guint8 r, guint8 g, guint8 b, guint8 a);
+/**
+ * @brief Draws the sub-segment of the specified line segment that lies within the
+ * clip region into the pixbuf.
+ * @param pb The `GdkPixbuf` to paint into.
+ * @param clip_x,clip_y Coordinates of the top-left corner of the clipping region.
+ * @param clip_w,clip_h Extent of the clipping region.
+ * @param x1,y1 Coordinates of the first point of the line segment.
+ * @param x2,y2 Coordinates of the second point of the line segment.
+ * @param r,g,b,a Color and alpha.
+ */
void pixbuf_draw_line(GdkPixbuf *pb,
gint clip_x, gint clip_y, gint clip_w, gint clip_h,
gint x1, gint y1, gint x2, gint y2,
guint8 r, guint8 g, guint8 b, guint8 a);
+/**
+ * @brief Composites a "shaded" region of the specified color and with the
+ * specified size and border gradient width into the clip region of the
+ * specified pixbuf.
+ * @param pb The `GdkPixbuf` to paint into.
+ * @param clip_x,clip_y Coordinates of the top-left corner of the clipping region.
+ * @param clip_w,clip_h Extent of the clipping region.
+ * @param x,y Coordinates of the top-left corner of the shaded region.
+ * @param w,h Extent of the shaded region.
+ * @param border The thickness, in pixels, of the gradient border around the
+ * fully-shaded region.
+ * @param r,g,b Shadow base color.
+ * @param a The max shadow composition fraction. Note that any alpha value of the
+ * original pixel will remain untouched.
+ */
void pixbuf_draw_shadow(GdkPixbuf *pb,
gint clip_x, gint clip_y, gint clip_w, gint clip_h,
gint x, gint y, gint w, gint h, gint border,
guint8 r, guint8 g, guint8 b, guint8 a);
+/**
+ * @brief Sets the r, g, and b values for each pixel within the specified region
+ * of the pixbuf the average of the original values for that pixel.
+ * @param pb The `GdkPixbuf` to paint into.
+ * @param x,y Coordinates of the top-left corner of the region.
+ * @param w,h Extent of the region.
+ */
void pixbuf_desaturate_rect(GdkPixbuf *pb,
gint x, gint y, gint w, gint h);
+/**
+ * @brief Sets each full-black `(0, 0, 0)` or full-white `(255, 255, 255)` pixel in the
+ * specified pixbuf region to full-red `(255, 0, 0)`. Does not change alpha.
+ * @param pb The `GdkPixbuf` to paint into.
+ * @param x,y Coordinates of the top-left corner of the first region.
+ * @param w,h Extent of the first region.
+ */
void pixbuf_highlight_overunderexposed(GdkPixbuf *pb,
- gint x, gint y, gint w, gint h);
+ gint x, gint y, gint w, gint h);
+/**
+ * @brief Sets the alpha channel to 255 (fully opaque) for every pixel in the specified
+ * pixbuf region.
+ * @param pb The `GdkPixbuf` to paint into.
+ * @param x,y Coordinates of the top-left corner of the first region.
+ * @param w,h Extent of the first region.
+ */
void pixbuf_ignore_alpha_rect(GdkPixbuf *pb,
- gint x, gint y, gint w, gint h);
+ gint x, gint y, gint w, gint h);
/* clipping utils */
+// TODO(xsdg): Rename this function to util_intersect_regions.
+/**
+ * @brief Performs an intersection of the two specified regions.
+ * @param[in] x,y Coordinates of the top-left corner of the first region.
+ * @param[in] w,h Extent of the first region.
+ * @param[in] clip_x,clip_y Coordinates of the top-left corner of the second region.
+ * @param[in] clip_w,clip_h Extent of the second region.
+ * @param[out] rx,ry Computed coordinates of the top-left corner of the intersection.
+ * @param[out] rw,rh Computed extent of the intersection.
+ * @retval FALSE The specified regions are non-overlapping.
+ * @retval TRUE The intersection operation was performed, and the output params were set.
+ */
gboolean util_clip_region(gint x, gint y, gint w, gint h,
gint clip_x, gint clip_y, gint clip_w, gint clip_h,
gint *rx, gint *ry, gint *rw, gint *rh);
+
+// TODO(xsdg): Rename this function to util_triangle_bounding_box.
+/**
+ * @brief Computes the bounding box for the specified triangle.
+ * @param[in] x1,y1 Coordinates of the first corner of the triangle.
+ * @param[in] x2,y2 Coordinates of the second corner of the triangle.
+ * @param[in] x3,y3 Coordinates of the third corner of the triangle.
+ * @param[out] rx,ry Computed coordinates of the top-left corner of the bounding box.
+ * @param[out] rw,rh Computed extent of the bounding box.
+ */
void util_clip_triangle(gint x1, gint y1, gint x2, gint y2, gint x3, gint y3,
gint &rx, gint &ry, gint &rw, gint &rh);