ext/psd_native/compose.c in psd_native-0.4.0 vs ext/psd_native/compose.c in psd_native-0.5.0
- old
+ new
@@ -1,40 +1,389 @@
+#include <math.h>
#include "psd_native_ext.h"
AlphaValues alpha;
VALUE psd_native_compose_normal(VALUE self, VALUE r_fg, VALUE r_bg, VALUE opts) {
- uint32_t fg = FIX2UINT(r_fg);
- uint32_t bg = FIX2UINT(r_bg);
- uint32_t new_r, new_g, new_b;
+ PIXEL fg = FIX2UINT(r_fg);
+ PIXEL bg = FIX2UINT(r_bg);
+ PIXEL new_r, new_g, new_b;
- if (opaque(fg) || transparent(bg)) return INT2FIX(fg);
- if (transparent(fg)) return INT2FIX(bg);
+ if (OPAQUE(fg) || TRANSPARENT(bg)) return r_fg;
+ if (TRANSPARENT(fg)) return r_bg;
calculate_alphas(fg, bg, &opts);
- new_r = blend_channel(r(bg), r(fg), alpha.mix);
- new_g = blend_channel(g(bg), g(fg), alpha.mix);
- new_b = blend_channel(b(bg), b(fg), alpha.mix);
+ new_r = BLEND_CHANNEL(R(bg), R(fg), alpha.mix);
+ new_g = BLEND_CHANNEL(G(bg), G(fg), alpha.mix);
+ new_b = BLEND_CHANNEL(B(bg), B(fg), alpha.mix);
- return INT2FIX(rgba(new_r, new_g, new_b, alpha.dst));
+ return INT2FIX(BUILD_PIXEL(new_r, new_g, new_b, alpha.dst));
}
-void calculate_alphas(uint32_t fg, uint32_t bg, VALUE *opts) {
+VALUE psd_native_compose_darken(VALUE self, VALUE r_fg, VALUE r_bg, VALUE opts) {
+ PIXEL fg = FIX2UINT(r_fg);
+ PIXEL bg = FIX2UINT(r_bg);
+ PIXEL new_r, new_g, new_b;
+
+ if (TRANSPARENT(bg)) return r_fg;
+ if (TRANSPARENT(fg)) return r_bg;
+
+ calculate_alphas(fg, bg, &opts);
+
+ new_r = R(fg) <= R(bg) ? BLEND_CHANNEL(R(bg), R(fg), alpha.mix) : R(bg);
+ new_g = G(fg) <= G(bg) ? BLEND_CHANNEL(G(bg), G(fg), alpha.mix) : G(bg);
+ new_b = B(fg) <= B(bg) ? BLEND_CHANNEL(B(bg), B(fg), alpha.mix) : B(bg);
+
+ return INT2FIX(BUILD_PIXEL(new_r, new_g, new_b, alpha.dst));
+}
+
+VALUE psd_native_compose_multiply(VALUE self, VALUE r_fg, VALUE r_bg, VALUE opts) {
+ PIXEL fg = FIX2UINT(r_fg);
+ PIXEL bg = FIX2UINT(r_bg);
+ PIXEL new_r, new_g, new_b;
+
+ if (TRANSPARENT(bg)) return r_fg;
+ if (TRANSPARENT(fg)) return r_bg;
+
+ calculate_alphas(fg, bg, &opts);
+
+ new_r = BLEND_CHANNEL(R(bg), R(fg) * R(bg) >> 8, alpha.mix);
+ new_g = BLEND_CHANNEL(G(bg), G(fg) * G(bg) >> 8, alpha.mix);
+ new_b = BLEND_CHANNEL(B(bg), B(fg) * B(bg) >> 8, alpha.mix);
+
+ return INT2FIX(BUILD_PIXEL(new_r, new_g, new_b, alpha.dst));
+}
+
+VALUE psd_native_compose_color_burn(VALUE self, VALUE r_fg, VALUE r_bg, VALUE opts) {
+ PIXEL fg = FIX2UINT(r_fg);
+ PIXEL bg = FIX2UINT(r_bg);
+ PIXEL new_r, new_g, new_b;
+
+ if (TRANSPARENT(bg)) return r_fg;
+ if (TRANSPARENT(fg)) return r_bg;
+
+ calculate_alphas(fg, bg, &opts);
+
+ new_r = BLEND_CHANNEL(R(bg), color_burn_foreground(R(bg), R(fg)), alpha.mix);
+ new_g = BLEND_CHANNEL(G(bg), color_burn_foreground(G(bg), G(fg)), alpha.mix);
+ new_b = BLEND_CHANNEL(B(bg), color_burn_foreground(B(bg), B(fg)), alpha.mix);
+
+ return INT2FIX(BUILD_PIXEL(new_r, new_g, new_b, alpha.dst));
+}
+
+PIXEL color_burn_foreground(PIXEL b, PIXEL f) {
+ if (f > 0) {
+ f = ((255 - b) << 8) / f;
+ return f > 255 ? 0 : (255 - f);
+ } else {
+ return b;
+ }
+}
+
+VALUE psd_native_compose_linear_burn(VALUE self, VALUE r_fg, VALUE r_bg, VALUE opts) {
+ PIXEL fg = FIX2UINT(r_fg);
+ PIXEL bg = FIX2UINT(r_bg);
+ PIXEL new_r, new_g, new_b;
+
+ if (TRANSPARENT(bg)) return r_fg;
+ if (TRANSPARENT(fg)) return r_bg;
+
+ calculate_alphas(fg, bg, &opts);
+
+ new_r = BLEND_CHANNEL(R(bg), (R(fg) < (255 - R(bg))) ? 0 : R(fg) - (255 - R(bg)), alpha.mix);
+ new_g = BLEND_CHANNEL(G(bg), (G(fg) < (255 - G(bg))) ? 0 : G(fg) - (255 - G(bg)), alpha.mix);
+ new_b = BLEND_CHANNEL(B(bg), (B(fg) < (255 - B(bg))) ? 0 : B(fg) - (255 - B(bg)), alpha.mix);
+
+ return INT2FIX(BUILD_PIXEL(new_r, new_g, new_b, alpha.dst));
+}
+
+VALUE psd_native_compose_lighten(VALUE self, VALUE r_fg, VALUE r_bg, VALUE opts) {
+ PIXEL fg = FIX2UINT(r_fg);
+ PIXEL bg = FIX2UINT(r_bg);
+ PIXEL new_r, new_g, new_b;
+
+ if (TRANSPARENT(bg)) return r_fg;
+ if (TRANSPARENT(fg)) return r_bg;
+
+ calculate_alphas(fg, bg, &opts);
+
+ new_r = (R(fg) >= R(bg)) ? BLEND_CHANNEL(R(bg), R(fg), alpha.mix) : R(bg);
+ new_g = (G(fg) >= G(bg)) ? BLEND_CHANNEL(G(bg), G(fg), alpha.mix) : G(bg);
+ new_b = (B(fg) >= B(bg)) ? BLEND_CHANNEL(B(bg), B(fg), alpha.mix) : B(bg);
+
+ return INT2FIX(BUILD_PIXEL(new_r, new_g, new_b, alpha.dst));
+}
+
+VALUE psd_native_compose_screen(VALUE self, VALUE r_fg, VALUE r_bg, VALUE opts) {
+ PIXEL fg = FIX2UINT(r_fg);
+ PIXEL bg = FIX2UINT(r_bg);
+ PIXEL new_r, new_g, new_b;
+
+ if (TRANSPARENT(bg)) return r_fg;
+ if (TRANSPARENT(fg)) return r_bg;
+
+ calculate_alphas(fg, bg, &opts);
+
+ new_r = BLEND_CHANNEL(R(bg), 255 - ((255 - R(bg)) * (255 - R(fg)) >> 8), alpha.mix);
+ new_g = BLEND_CHANNEL(G(bg), 255 - ((255 - G(bg)) * (255 - G(fg)) >> 8), alpha.mix);
+ new_b = BLEND_CHANNEL(B(bg), 255 - ((255 - B(bg)) * (255 - B(fg)) >> 8), alpha.mix);
+
+ return INT2FIX(BUILD_PIXEL(new_r, new_g, new_b, alpha.dst));
+}
+
+VALUE psd_native_compose_color_dodge(VALUE self, VALUE r_fg, VALUE r_bg, VALUE opts) {
+ PIXEL fg = FIX2UINT(r_fg);
+ PIXEL bg = FIX2UINT(r_bg);
+ PIXEL new_r, new_g, new_b;
+
+ if (TRANSPARENT(bg)) return r_fg;
+ if (TRANSPARENT(fg)) return r_bg;
+
+ calculate_alphas(fg, bg, &opts);
+
+ new_r = BLEND_CHANNEL(R(bg), color_dodge_foreground(R(bg), R(fg)), alpha.mix);
+ new_g = BLEND_CHANNEL(G(bg), color_dodge_foreground(G(bg), G(fg)), alpha.mix);
+ new_b = BLEND_CHANNEL(B(bg), color_dodge_foreground(B(bg), B(fg)), alpha.mix);
+
+ return INT2FIX(BUILD_PIXEL(new_r, new_g, new_b, alpha.dst));
+}
+
+PIXEL color_dodge_foreground(PIXEL b, PIXEL f) {
+ if (f < 255) {
+ return CLAMP_PIXEL((b << 8) / (255 - f));
+ } else {
+ return b;
+ }
+}
+
+VALUE psd_native_compose_linear_dodge(VALUE self, VALUE r_fg, VALUE r_bg, VALUE opts) {
+ PIXEL fg = FIX2UINT(r_fg);
+ PIXEL bg = FIX2UINT(r_bg);
+ PIXEL new_r, new_g, new_b;
+
+ if (TRANSPARENT(bg)) return r_fg;
+ if (TRANSPARENT(fg)) return r_bg;
+
+ calculate_alphas(fg, bg, &opts);
+
+ new_r = BLEND_CHANNEL(R(bg), (R(bg) + R(fg)) > 255 ? 255 : R(bg) + R(fg), alpha.mix);
+ new_g = BLEND_CHANNEL(G(bg), (G(bg) + G(fg)) > 255 ? 255 : G(bg) + G(fg), alpha.mix);
+ new_b = BLEND_CHANNEL(B(bg), (B(bg) + B(fg)) > 255 ? 255 : B(bg) + B(fg), alpha.mix);
+
+ return INT2FIX(BUILD_PIXEL(new_r, new_g, new_b, alpha.dst));
+}
+
+VALUE psd_native_compose_overlay(VALUE self, VALUE r_fg, VALUE r_bg, VALUE opts) {
+ PIXEL fg = FIX2UINT(r_fg);
+ PIXEL bg = FIX2UINT(r_bg);
+ PIXEL new_r, new_g, new_b;
+
+ if (TRANSPARENT(bg)) return r_fg;
+ if (TRANSPARENT(fg)) return r_bg;
+
+ calculate_alphas(fg, bg, &opts);
+
+ new_r = BLEND_CHANNEL(R(bg), overlay_foreground(R(bg), R(fg)), alpha.mix);
+ new_g = BLEND_CHANNEL(G(bg), overlay_foreground(G(bg), G(fg)), alpha.mix);
+ new_b = BLEND_CHANNEL(B(bg), overlay_foreground(B(bg), B(fg)), alpha.mix);
+
+ return INT2FIX(BUILD_PIXEL(new_r, new_g, new_b, alpha.dst));
+}
+
+PIXEL overlay_foreground(PIXEL b, PIXEL f) {
+ if (b < 128) {
+ return b * f >> 7;
+ } else {
+ return 255 - ((255 - b) * (255 - f) >> 7);
+ }
+}
+
+VALUE psd_native_compose_soft_light(VALUE self, VALUE r_fg, VALUE r_bg, VALUE opts) {
+ PIXEL fg = FIX2UINT(r_fg);
+ PIXEL bg = FIX2UINT(r_bg);
+ PIXEL new_r, new_g, new_b;
+
+ if (TRANSPARENT(bg)) return r_fg;
+ if (TRANSPARENT(fg)) return r_bg;
+
+ calculate_alphas(fg, bg, &opts);
+
+ new_r = BLEND_CHANNEL(R(bg), soft_light_foreground(R(bg), R(fg)), alpha.mix);
+ new_g = BLEND_CHANNEL(G(bg), soft_light_foreground(G(bg), G(fg)), alpha.mix);
+ new_b = BLEND_CHANNEL(B(bg), soft_light_foreground(B(bg), B(fg)), alpha.mix);
+
+ return INT2FIX(BUILD_PIXEL(new_r, new_g, new_b, alpha.dst));
+}
+
+PIXEL soft_light_foreground(PIXEL b, PIXEL f) {
+ uint32_t c1 = b * f >> 8;
+ uint32_t c2 = 255 - ((255 - b) * (255 - f) >> 8);
+ return ((255 - b) * c1 >> 8) + (b * c2 >> 8);
+}
+
+VALUE psd_native_compose_hard_light(VALUE self, VALUE r_fg, VALUE r_bg, VALUE opts) {
+ PIXEL fg = FIX2UINT(r_fg);
+ PIXEL bg = FIX2UINT(r_bg);
+ PIXEL new_r, new_g, new_b;
+
+ if (TRANSPARENT(bg)) return r_fg;
+ if (TRANSPARENT(fg)) return r_bg;
+
+ calculate_alphas(fg, bg, &opts);
+
+ new_r = BLEND_CHANNEL(R(bg), hard_light_foreground(R(bg), R(fg)), alpha.mix);
+ new_g = BLEND_CHANNEL(G(bg), hard_light_foreground(G(bg), G(fg)), alpha.mix);
+ new_b = BLEND_CHANNEL(B(bg), hard_light_foreground(B(bg), B(fg)), alpha.mix);
+
+ return INT2FIX(BUILD_PIXEL(new_r, new_g, new_b, alpha.dst));
+}
+
+PIXEL hard_light_foreground(PIXEL b, PIXEL f) {
+ if (f < 128) {
+ return b * f >> 7;
+ } else {
+ return 255 - ((255 - f) * (255 - b) >> 7);
+ }
+}
+
+VALUE psd_native_compose_vivid_light(VALUE self, VALUE r_fg, VALUE r_bg, VALUE opts) {
+ PIXEL fg = FIX2UINT(r_fg);
+ PIXEL bg = FIX2UINT(r_bg);
+ PIXEL new_r, new_g, new_b;
+
+ if (TRANSPARENT(bg)) return r_fg;
+ if (TRANSPARENT(fg)) return r_bg;
+
+ calculate_alphas(fg, bg, &opts);
+
+ new_r = BLEND_CHANNEL(R(bg), vivid_light_foreground(R(bg), R(fg)), alpha.mix);
+ new_g = BLEND_CHANNEL(G(bg), vivid_light_foreground(G(bg), G(fg)), alpha.mix);
+ new_b = BLEND_CHANNEL(B(bg), vivid_light_foreground(B(bg), B(fg)), alpha.mix);
+
+ return INT2FIX(BUILD_PIXEL(new_r, new_g, new_b, alpha.dst));
+}
+
+PIXEL vivid_light_foreground(PIXEL b, PIXEL f) {
+ if (f < 255) {
+ return CLAMP_PIXEL((b * b / (255 - f) + f * f / (255 - b)) >> 1);
+ } else {
+ return b;
+ }
+}
+
+VALUE psd_native_compose_linear_light(VALUE self, VALUE r_fg, VALUE r_bg, VALUE opts) {
+ PIXEL fg = FIX2UINT(r_fg);
+ PIXEL bg = FIX2UINT(r_bg);
+ PIXEL new_r, new_g, new_b;
+
+ if (TRANSPARENT(bg)) return r_fg;
+ if (TRANSPARENT(fg)) return r_bg;
+
+ calculate_alphas(fg, bg, &opts);
+
+ new_r = BLEND_CHANNEL(R(bg), linear_light_foreground(R(bg), R(fg)), alpha.mix);
+ new_g = BLEND_CHANNEL(G(bg), linear_light_foreground(G(bg), G(fg)), alpha.mix);
+ new_b = BLEND_CHANNEL(B(bg), linear_light_foreground(B(bg), B(fg)), alpha.mix);
+
+ return INT2FIX(BUILD_PIXEL(new_r, new_g, new_b, alpha.dst));
+}
+
+PIXEL linear_light_foreground(PIXEL b, PIXEL f) {
+ if (b < 255) {
+ return CLAMP_PIXEL(f * f / (255 - b));
+ } else {
+ return 255;
+ }
+}
+
+VALUE psd_native_compose_pin_light(VALUE self, VALUE r_fg, VALUE r_bg, VALUE opts) {
+ PIXEL fg = FIX2UINT(r_fg);
+ PIXEL bg = FIX2UINT(r_bg);
+ PIXEL new_r, new_g, new_b;
+
+ if (TRANSPARENT(bg)) return r_fg;
+ if (TRANSPARENT(fg)) return r_bg;
+
+ calculate_alphas(fg, bg, &opts);
+
+ new_r = BLEND_CHANNEL(R(bg), pin_light_foreground(R(bg), R(fg)), alpha.mix);
+ new_g = BLEND_CHANNEL(G(bg), pin_light_foreground(G(bg), G(fg)), alpha.mix);
+ new_b = BLEND_CHANNEL(B(bg), pin_light_foreground(B(bg), B(fg)), alpha.mix);
+
+ return INT2FIX(BUILD_PIXEL(new_r, new_g, new_b, alpha.dst));
+}
+
+PIXEL pin_light_foreground(PIXEL b, PIXEL f) {
+ if (f >= 128) {
+ return PSD_MAX(b, (f - 128) * 2);
+ } else {
+ return PSD_MIN(b, f * 2);
+ }
+}
+
+VALUE psd_native_compose_hard_mix(VALUE self, VALUE r_fg, VALUE r_bg, VALUE opts) {
+ PIXEL fg = FIX2UINT(r_fg);
+ PIXEL bg = FIX2UINT(r_bg);
+ PIXEL new_r, new_g, new_b;
+
+ if (TRANSPARENT(bg)) return r_fg;
+ if (TRANSPARENT(fg)) return r_bg;
+
+ calculate_alphas(fg, bg, &opts);
+
+ new_r = BLEND_CHANNEL(R(bg), (R(bg) + R(fg) <= 255) ? 0 : 255, alpha.mix);
+ new_g = BLEND_CHANNEL(G(bg), (G(bg) + G(fg) <= 255) ? 0 : 255, alpha.mix);
+ new_b = BLEND_CHANNEL(B(bg), (B(bg) + B(fg) <= 255) ? 0 : 255, alpha.mix);
+
+ return INT2FIX(BUILD_PIXEL(new_r, new_g, new_b, alpha.dst));
+}
+
+VALUE psd_native_compose_difference(VALUE self, VALUE r_fg, VALUE r_bg, VALUE opts) {
+ PIXEL fg = FIX2UINT(r_fg);
+ PIXEL bg = FIX2UINT(r_bg);
+ PIXEL new_r, new_g, new_b;
+
+ if (TRANSPARENT(bg)) return r_fg;
+ if (TRANSPARENT(fg)) return r_bg;
+
+ calculate_alphas(fg, bg, &opts);
+
+ new_r = BLEND_CHANNEL(R(bg), abs(R(bg) - R(fg)), alpha.mix);
+ new_g = BLEND_CHANNEL(G(bg), abs(G(bg) - G(fg)), alpha.mix);
+ new_b = BLEND_CHANNEL(B(bg), abs(B(bg) - B(fg)), alpha.mix);
+
+ return INT2FIX(BUILD_PIXEL(new_r, new_g, new_b, alpha.dst));
+}
+
+VALUE psd_native_compose_exclusion(VALUE self, VALUE r_fg, VALUE r_bg, VALUE opts) {
+ PIXEL fg = FIX2UINT(r_fg);
+ PIXEL bg = FIX2UINT(r_bg);
+ PIXEL new_r, new_g, new_b;
+
+ if (TRANSPARENT(bg)) return r_fg;
+ if (TRANSPARENT(fg)) return r_bg;
+
+ calculate_alphas(fg, bg, &opts);
+
+ new_r = BLEND_CHANNEL(R(bg), R(bg) + R(fg) - (R(bg) * R(fg) >> 7), alpha.mix);
+ new_g = BLEND_CHANNEL(G(bg), G(bg) + G(fg) - (G(bg) * G(fg) >> 7), alpha.mix);
+ new_b = BLEND_CHANNEL(B(bg), B(bg) + B(fg) - (B(bg) * B(fg) >> 7), alpha.mix);
+
+ return INT2FIX(BUILD_PIXEL(new_r, new_g, new_b, alpha.dst));
+}
+
+void calculate_alphas(PIXEL fg, PIXEL bg, VALUE *opts) {
uint32_t opacity = calculate_opacity(opts);
- uint32_t src_alpha = a(fg) * opacity >> 8;
+ uint32_t src_alpha = A(fg) * opacity >> 8;
- alpha.dst = a(bg);
+ alpha.dst = A(bg);
alpha.mix = (src_alpha << 8) / (src_alpha + ((256 - src_alpha) * alpha.dst >> 8));
alpha.dst = alpha.dst + ((256 - alpha.dst) * src_alpha >> 8);
}
uint32_t calculate_opacity(VALUE *opts) {
uint32_t opacity = FIX2UINT(rb_hash_aref(*opts, ID2SYM(rb_intern("opacity"))));
uint32_t fill_opacity = FIX2UINT(rb_hash_aref(*opts, ID2SYM(rb_intern("fill_opacity"))));
return opacity * fill_opacity / 255;
-}
-
-uint32_t blend_channel(uint32_t bg, uint32_t fg, uint32_t a) {
- return ((bg << 8) + (fg - bg) * a) >> 8;
}
\ No newline at end of file