Skip to content

Image Operator Menu #176

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
119 changes: 105 additions & 14 deletions korman/operators/op_image.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,89 @@
}

class ImageOperator:
def execute(self, context):
with ConsoleToggler(True), ExportProgressLogger() as self._report:
try:
self._execute(context)
except ExportError as error:
self.report({"ERROR"}, str(error))
return {"CANCELLED"}
else:
return {"FINISHED"}

def _execute(self, context):
raise NotImplementedError()

@classmethod
def poll(cls, context):
return context.scene.render.engine == "PLASMA_GAME"


class PlasmaBakeImageAlphaOperator(ImageOperator, bpy.types.Operator):
bl_idname = "image.plasma_bake_image_alpha"
bl_label = "Bake Image Alpha"
bl_description = "Bake an image's calculated alpha to another image's alpha channel"

filepath = StringProperty(name="Alpha Image",
subtype="FILE_PATH",
options=set())

def _execute(self, context):
self._report.progress_add_step("Preparing Images")
self._report.progress_add_step("Baking Alpha")
self._report.progress_add_step("Creating Image")
self._report.progress_start("BAKING IMAGE ALPHA")
if not Path(self.filepath).is_file():
raise ExportError("No image found at '{}'".format(self.filepath))

self._report.progress_advance()
images, im_diffuse = bpy.data.images, context.space_data.image
with TemporaryObject(images.load(self.filepath), images.remove) as im_alpha:
width, height = self._check_image_scale(im_diffuse, im_alpha)

self._report.progress_advance()
pixels = self._calc_alpha(im_diffuse, im_alpha, width, height)

self._report.progress_advance()
self._make_image(im_diffuse.name, self.filepath, width, height, pixels, context)
self._report.progress_end()

def _check_image_scale(self, im_diffuse, im_alpha):
sz_diffuse, sz_alpha = tuple(im_diffuse.size), tuple(im_alpha.size)
if tuple(im_diffuse.size) != tuple(im_alpha.size):
self._report.warn("Image sizes do not match, rescaling alpha image")
im_alpha.scale(*sz_diffuse)
return sz_diffuse

def _calc_alpha(self, im_diffuse, im_alpha, width, height):
# DO NOT REMOVE this copying... Trying to operate directly on the bpy_prop_collection
# is slower than molasses running uphill in Siberia!
pixels_diffuse, pixels_alpha = list(im_diffuse.pixels), tuple(im_alpha.pixels)
for i in range(width * height):
alpha_idx = i * 4
pixels_diffuse[alpha_idx + 3] = sum(pixels_alpha[alpha_idx:alpha_idx+2]) / 3.0
return pixels_diffuse

def _make_image(self, name_diffuse, name_alpha, width, height, pixels, context=None):
name = "{}_ALPHAGEN_{}.png".format(Path(name_diffuse).stem, Path(name_alpha).stem)
im_dest = bpy.data.images.new(name, width, height, True)
im_dest.pixels = pixels
im_dest.update()
im_dest.pack(as_png=True)
if context and context.space_data:
context.space_data.image = im_dest
return im_dest

def invoke(self, context, event):
context.window_manager.fileselect_add(self)
return {"RUNNING_MODAL"}

@classmethod
def poll(cls, context):
space = context.space_data
return super().poll(context) and space and space.image


class PlasmaBuildCubeMapOperator(ImageOperator, bpy.types.Operator):
bl_idname = "image.plasma_build_cube_map"
bl_label = "Build Cubemap"
Expand All @@ -58,24 +136,11 @@ class PlasmaBuildCubeMapOperator(ImageOperator, bpy.types.Operator):
default="",
options={"HIDDEN"})

def __init__(self):
self._report = ExportProgressLogger()
def _execute(self, context):
self._report.progress_add_step("Finding Face Images")
self._report.progress_add_step("Loading Face Images")
self._report.progress_add_step("Scaling Face Images")
self._report.progress_add_step("Generating Cube Map")

def execute(self, context):
with ConsoleToggler(True) as _:
try:
self._execute()
except ExportError as error:
self.report({"ERROR"}, str(error))
return {"CANCELLED"}
else:
return {"FINISHED"}

def _execute(self):
self._report.progress_start("BUILDING CUBE MAP")
if not Path(self.filepath).is_file():
raise ExportError("No cube image found at '{}'".format(self.filepath))
Expand Down Expand Up @@ -235,3 +300,29 @@ def _scale_images(self, face_widths, face_heights, face_data):
min_width, min_height)
self._report.progress_increment()
return min_width, min_height, tuple(result_data)


class PlasmaClampAlphaOperator(ImageOperator, bpy.types.Operator):
bl_idname = "image.plasma_clamp_image_alpha"
bl_label = "Clamp Image Alpha"
bl_description = "Clamp an image's alpha to fully opaque or transparent"
bl_options = {"UNDO"}

def _execute(self, context):
self._report.progress_add_step("Clamping Alpha")
self._report.progress_start("CLAMPING ALPHA")
self._report.progress_advance()

image = context.space_data.image
pixels = list(image.pixels)
width, height = image.size
# significant figures: round up the even
for i in range(3, (width * height * 4), 4):
pixels[i] = 1.0 if pixels[i] >= 0.5 else 0.0
image.pixels = pixels
self._report.progress_end()

@classmethod
def poll(cls, context):
space = context.space_data
return super().poll(context) and space and space.image
30 changes: 25 additions & 5 deletions korman/ui/ui_menus.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@
# You should have received a copy of the GNU General Public License
# along with Korman. If not, see <http://www.gnu.org/licenses/>.

from ..operators.op_mesh import *
import bpy
import functools

class PlasmaMenu:
@classmethod
Expand All @@ -32,14 +33,33 @@ def draw(self, context):
layout.operator("mesh.plasma_ladder_add", text="Ladder", icon="COLLAPSEMENU")


def build_plasma_menu(self, context):
class PlasmaImageMenu(PlasmaMenu, bpy.types.Menu):
bl_idname = "menu.plasma_image"
bl_label = "Plasma"
bl_description = "Plasma Image Operators"

def draw(self, context):
layout = self.layout

layout.operator("image.plasma_bake_image_alpha", icon="IMAGE_RGB_ALPHA")
layout.operator("image.plasma_clamp_image_alpha", icon="IMAGE_ALPHA")
layout.separator()
layout.operator("image.plasma_build_cube_map", icon="MATCUBE")


def _build_plasma_menu(menu_operator, self, context):
if context.scene.render.engine != "PLASMA_GAME":
return
self.layout.separator()
self.layout.menu("menu.plasma_add", icon="URL")
self.layout.menu(menu_operator, icon="URL")

build_plasma_add_menu = functools.partial(_build_plasma_menu, "menu.plasma_add")
build_plasma_image_menu = functools.partial(_build_plasma_menu, "menu.plasma_image")

def register():
bpy.types.INFO_MT_add.append(build_plasma_menu)
bpy.types.INFO_MT_add.append(build_plasma_add_menu)
bpy.types.IMAGE_MT_image.append(build_plasma_image_menu)

def unregister():
bpy.types.INFO_MT_add.remove(build_plasma_menu)
bpy.types.INFO_MT_add.remove(build_plasma_add_menu)
bpy.types.IMAGE_MT_image.remove(build_plasma_image_menu)