OSDN Git Service

layer compositing: svg op naming, rgba-on-rgba
authorsigetch <sigetch@users.sourceforge.jp>
Mon, 19 Sep 2011 15:56:31 +0000 (00:56 +0900)
committerAndrew Chadwick <andrewc-git@piffle.org>
Sat, 24 Sep 2011 19:12:52 +0000 (20:12 +0100)
Change composite-op name to be compatible with SVG spec. name.

Initial import of layer composition for RGBA x RGBA operation (some are
not implemented properly.)

lib/document.py
lib/layer.py
lib/tiledsurface.py

index 950d688..faa3eea 100644 (file)
@@ -21,6 +21,18 @@ import brush
 N = tiledsurface.N
 LOAD_CHUNK_SIZE = 64*1024
 
+LAYER_MODE_DICT = {
+  "svg:over" : "normal",
+  "svg:multiply" : "multiply",
+  "svg:color-burn" : "burn",
+  "svg:color-dodge" : "dodge",
+  "svg:screen" : "screen"
+}
+
+REVERSE_LAYER_MODE_DICT = {}
+for key in LAYER_MODE_DICT.keys():
+    REVERSE_LAYER_MODE_DICT[LAYER_MODE_DICT[key]] = key
+
 class SaveLoadError(Exception):
     """Expected errors on loading or saving, like missing permissions or non-existing files."""
     pass
@@ -511,7 +523,12 @@ class Document():
             a['x'] = str(x)
             a['y'] = str(y)
             a['opacity'] = str(opac)
-            a['composite-op'] = str(compositeop)
+            composite_op_str = "svg:over"
+            try:
+                composite_op_str = REVERSE_LAYER_MODE_DICT[compositeop]
+            except KeyError:
+                composite_op_str = "svg:over"
+            a['composite-op'] = composite_op_str
             if visible:
                 a['visibility'] = 'visible'
             else:
@@ -644,7 +661,15 @@ class Document():
             x = int(a.get('x', '0'))
             y = int(a.get('y', '0'))
             opac = float(a.get('opacity', '1.0'))
-            compositeop = a.get('composite-op', 'over')
+            compositeop_raw = a.get('composite-op', 'svg:over')
+            try:
+                compositeop = LAYER_MODE_DICT[compositeop_raw]
+            except KeyError:
+                if compositeop_raw in REVERSE_LAYER_MODE_DICT:
+                    compositeop = compositeop_raw
+                else:
+                    compositeop = "normal"
+                    
             visible = not 'hidden' in a.get('visibility', 'visible')
             self.add_layer(insert_idx=0, name=name)
             last_pixbuf = pixbuf
index 0b96dc5..d8ff7d1 100644 (file)
@@ -106,7 +106,7 @@ class Layer:
         for tx, ty in src.surface.get_tiles():
             surf = dst.surface.get_tile_memory(tx, ty, readonly=False)
             #src.surface.composite_tile_over(surf, tx, ty, opacity=self.effective_opacity)
-            src.surface.composite_tile(surf, tx, ty, opacity=self.effective_opacity, compositeop=self.compositeop)
+            src.surface.composite_tile(surf, tx, ty, opacity=self.effective_opacity, mode=self.compositeop)
         dst.opacity = 1.0
 
     def get_stroke_info_at(self, x, y):
index 5855dfa..c6c508e 100644 (file)
@@ -155,8 +155,15 @@ class Surface(mypaintlib.TiledSurface):
             # rarely used (for merging layers, also when exporting a transparent PNGs)
             # src (premultiplied) OVER dst (premultiplied)
             # dstColor = srcColor + (1.0 - srcAlpha) * dstColor
-            one_minus_srcAlpha = (1<<15) - (opacity * src[:,:,3:4]).astype('uint32')
-            dst[:,:,:] = opacity * src[:,:,:] + ((one_minus_srcAlpha * dst[:,:,:]) >> 15).astype('uint16')
+            srcAlpha = (opacity * src[:,:,3:4]).astype('float') / (1 << 15)
+            dstAlpha = (dst[:,:,3:4]).astype('float') / (1 << 15)
+            src_premult = srcAlpha * src[:,:,0:3].astype('float') / (1<<15)
+            dst_premult = dstAlpha * dst[:,:,0:3].astype('float') / (1<<15)
+            dst_c = clip(src_premult + (1.0 - srcAlpha) * dst_premult, 0.0, 1.0)
+            dst_a = clip(srcAlpha + dstAlpha - srcAlpha * dstAlpha, 0.0, 1.0)
+            dst_c = where(dst_a == 0, 1.0, dst_c / dst_a)
+            dst[:,:,0:3] = clip(dst_c * (1<<15), 0, (1<<15) - 1).astype('uint16')
+            dst[:,:,3:4] = clip(dst_a * (1<<15), 0, (1<<15) - 1).astype('uint16')
         elif dst.shape[2] == 3 and dst.dtype == 'uint16':
             mypaintlib.tile_composite_rgba16_over_rgb16(src, dst, opacity)
         else:
@@ -175,9 +182,16 @@ class Surface(mypaintlib.TiledSurface):
         if dst.shape[2] == 4 and dst.dtype == 'uint16':
             # rarely used (for merging layers, also when exporting a transparent PNGs)
             # src (premultiplied) MULTIPLY dst (premultiplied)
-            # dstColor = srcColor * dstColor + (1.0 - srcAlpha) * dstColor
-            one_minus_srcAlpha = (1<<15) - (opacity * src[:,:,3:4]).astype('uint32')
-            dst[:,:,:] = dst[:,:,:] * src[:,:,:] + ((one_minus_srcAlpha * dst[:,:,:]) >> 15).astype('uint16')
+            # cA * cB +  cA * (1 - aB) + cB * (1 - aA)
+            srcAlpha = (opacity * src[:,:,3:4]).astype('float') / (1 << 15)
+            dstAlpha = (dst[:,:,3:4]).astype('float') / (1 << 15)
+            src_premult = srcAlpha * src[:,:,0:3].astype('float') / (1<<15)
+            dst_premult = dstAlpha * dst[:,:,0:3].astype('float') / (1<<15)
+            dst_c = clip(src_premult * dst_premult +  src_premult * (1.0 - dstAlpha) + dst_premult * (1.0 - srcAlpha),0.0, 1.0)
+            dst_a = clip(srcAlpha + dstAlpha - srcAlpha * dstAlpha, 0.0, 1.0)
+            dst_c = where(dst_a == 0, 1.0, dst_c / dst_a)
+            dst[:,:,0:3] = clip(dst_c * (1<<15), 0, (1<<15) - 1).astype('uint16')
+            dst[:,:,3:4] = clip(dst_a * (1<<15), 0, (1<<15) - 1).astype('uint16')
         elif dst.shape[2] == 3 and dst.dtype == 'uint16':
             mypaintlib.tile_composite_rgba16_multiply_rgb16(src, dst, opacity)
         else:
@@ -196,9 +210,16 @@ class Surface(mypaintlib.TiledSurface):
         if dst.shape[2] == 4 and dst.dtype == 'uint16':
             # rarely used (for merging layers, also when exporting a transparent PNGs)
             # src (premultiplied) SCREEN dst (premultiplied)
-            # dstColor = srcColor + (1.0 - srcAlpha) * dstColor
-            one_minus_srcAlpha = (1<<15) - (opacity * src[:,:,3:4]).astype('uint32')
-            dst[:,:,:] = opacity * src[:,:,:] + dst[:,:,:] - ((src[:,:,:] * dst[:,:,:]) >> 15).astype('uint16')
+            # cA + cB - cA * cB
+            srcAlpha = (opacity * src[:,:,3:4]).astype('float') / (1 << 15)
+            dstAlpha = (dst[:,:,3:4]).astype('float') / (1 << 15)
+            src_premult = srcAlpha * src[:,:,0:3].astype('float') / (1<<15)
+            dst_premult = dstAlpha * dst[:,:,0:3].astype('float') / (1<<15)
+            dst_c = clip(src_premult + dst_premult - src_premult * dst_premult, 0, 1)
+            dst_a = clip(srcAlpha + dstAlpha - srcAlpha * dstAlpha, 0, 1)
+            dst_c = where(dst_a == 0, 1.0, dst_c / dst_a)
+            dst[:,:,0:3] = clip(dst_c * (1<<15),0, (1<<15) - 1).astype('uint16')
+            dst[:,:,3:4] = clip(dst_a * (1<<15),0, (1<<15) - 1).astype('uint16')
         elif dst.shape[2] == 3 and dst.dtype == 'uint16':
             mypaintlib.tile_composite_rgba16_screen_rgb16(src, dst, opacity)
         else:
@@ -217,9 +238,23 @@ class Surface(mypaintlib.TiledSurface):
         if dst.shape[2] == 4 and dst.dtype == 'uint16':
             # rarely used (for merging layers, also when exporting a transparent PNGs)
             # src (premultiplied) OVER dst (premultiplied)
-            # dstColor = srcColor + (1.0 - srcAlpha) * dstColor
-            one_minus_srcAlpha = (1<<15) - (opacity * src[:,:,3:4]).astype('uint32')
-            dst[:,:,:] = opacity * src[:,:,:] + ((one_minus_srcAlpha * dst[:,:,:]) >> 15).astype('uint16')
+            # if cA * aB + cB * aA <= aA * aB : 
+            #   cA * (1 - aB) + cB * (1 - aA)
+            #   (cA == 0 ? 1 : (aA * (cA * aB + cB * aA - aA * aB) / cA) + cA * (1 - aB) + cB * (1 - aA))
+            #  where B = dst, A = src
+            aA = (opacity * src[:,:,3:4]).astype('float') / (1 << 15)
+            aB = (dst[:,:,3:4]).astype('float') / (1 << 15)
+            cA = aA * src[:,:,0:3].astype('float') / (1<<15)
+            cB = aB * dst[:,:,0:3].astype('float') / (1<<15)
+            dst_c = where(cA * aB + cB * aA <= aA * aB,
+                         cA * (1 - aB) + cB * (1 - aA),
+                         where(cA == 0,
+                               1.0,
+                               (aA * (cA * aB + cB * aA - aA * aB) / cA) + cA * (1.0 - aB) + cB * (1.0 - aA)))
+            dst_a = aA + aB - aA * aB
+            dst_c = where(dst_a == 0, 1.0, dst_c / dst_a)
+            dst[:,:,0:3] = clip(dst_c * (1<<15), 0, (1<<15) - 1).astype('uint16')
+            dst[:,:,3:4] = clip(dst_a * (1<<15), 0, (1<<15) - 1).astype('uint16')
         elif dst.shape[2] == 3 and dst.dtype == 'uint16':
             mypaintlib.tile_composite_rgba16_burn_rgb16(src, dst, opacity)
         else:
@@ -238,9 +273,21 @@ class Surface(mypaintlib.TiledSurface):
         if dst.shape[2] == 4 and dst.dtype == 'uint16':
             # rarely used (for merging layers, also when exporting a transparent PNGs)
             # src (premultiplied) OVER dst (premultiplied)
-            # dstColor = srcColor + (1.0 - srcAlpha) * dstColor
-            one_minus_srcAlpha = (1<<15) - (opacity * src[:,:,3:4]).astype('uint32')
-            dst[:,:,:] = opacity * src[:,:,:] + ((one_minus_srcAlpha * dst[:,:,:]) >> 15).astype('uint16')
+            # if cA * aB + cB * aA >= aA * aB : 
+            #   aA * aB + cA * (1 - aB) + cB * (1 - aA)
+            #   (cA == aA ? 1 : cB * aA / (aA == 0 ? 1 : 1 - cA / aA)) + cA * (1 - aB) + cB * (1 - aA)
+            #  where B = dst, A = src
+            aA = (opacity * src[:,:,3:4]).astype('float') / (1 << 15)
+            aB = (dst[:,:,3:4]).astype('float') / (1 << 15)
+            cA = aA * src[:,:,0:3].astype('float') / (1<<15)
+            cB = aB * dst[:,:,0:3].astype('float') / (1<<15)
+            dst_c = where(cA * aB + cB * aA >= aA * aB,
+                         aA * aB + cA * (1 - aB) + cB * (1 - aA),
+                         where(cA == aA, 1.0, cB * aA / where(aA == 0, 1.0, 1.0 - cA / aA)) + cA * (1.0 - aB) + cB * (1.0 - aA))
+            dst_a = (aA + aB - aA * aB)
+            dst_c = where(dst_a == 0, 1.0, dst_c / dst_a)
+            dst[:,:,0:3] = clip(dst_c * (1<<15),0, (1<<15) - 1).astype('uint16')
+            dst[:,:,3:4] = clip(dst_a * (1<<15),0, (1<<15) - 1).astype('uint16')
         elif dst.shape[2] == 3 and dst.dtype == 'uint16':
             mypaintlib.tile_composite_rgba16_dodge_rgb16(src, dst, opacity)
         else: