def test_can_save_stretched_editable_image_as_png_to_disk(self): dummy_png = self.img_proc_pngfile_paths()[0] buffered_image = img_proc.read_editable_image_from_disk(dummy_png) assert isinstance(buffered_image, java.awt.image.BufferedImage) font = img_proc.create_font(self._allowed_font_names[0], 14.0, bold=False, italic=False) helvetica_font_value = 3 helvetica_font = img_proc.create_font(self._allowed_font_names[helvetica_font_value], 14.0, bold=False, italic=False) # check whether the left-outline of letter "H" in "Hello world" is NOT present in the image! for i in range(0, 8): rgb = buffered_image.getRGB(11, i) red = rgb >> 16 & int("0x000000FF", 16) green = rgb >> 8 & int("0x000000FF", 16) blue = rgb & int("0x000000FF", 16) alpha = (rgb>>24) & 0xff assert red == 0 and green == 0 and blue == 0 and alpha == 0 buffered_image = img_proc.add_text_to_image(buffered_image, "Hello world!", helvetica_font, Color.BLUE, 10.0, 10.0, 10.0, 95, 30) #buffered_image = img_proc.add_text_to_image(buffered_image, "Franz ist hier!?", marker_font, Color.BLUE,10.0, 20.0) #buffered_image = img_proc.add_text_to_image(buffered_image, "iloveKF", donegal_font, Color.GREEN,10.0, 32.0) #TODO make new Test for stretched pixels output_path = self.img_proc_pngfile_output_path("test_stretched.png") try: img_proc.save_editable_image_as_png_to_disk(buffered_image, output_path, overwrite=True) assert os.path.isfile(output_path) assert imghdr.what(output_path) == 'png' # Reload the image from disk now and check if left-outline of letter "H" is still present! new_buffered_image = img_proc.read_editable_image_from_disk(output_path) assert isinstance(new_buffered_image, java.awt.image.BufferedImage) #TODO make the same check as before you reload the image from disk except Exception, e: raise e
def test_can_add_stretched_text_to_editable_image(self): dummy_png = self.img_proc_pngfile_paths()[0] #buffered_image = img_proc.read_editable_image_from_disk(dummy_png) #assert isinstance(buffered_image, java.awt.image.BufferedImage) for font_name in self._allowed_font_names: font = img_proc.create_font(font_name, 14.0, bold=False, italic=False) # check whether the left-outline of letter "H" in "Hello world" is NOT present in the image! test_colors = { Color.BLUE: (0, 0, 255), Color.RED: (255, 0, 0), Color.WHITE: (255, 255, 255), Color.BLACK: (0,0,0) } for (color, value) in test_colors.iteritems(): buffered_image = img_proc.read_editable_image_from_disk(dummy_png) assert isinstance(buffered_image, java.awt.image.BufferedImage) #test1 8 pixel down for i in range(0, 15): rgb = buffered_image.getRGB(15, i) red = rgb >> 16 & int("0x000000FF", 16) green = rgb >> 8 & int("0x000000FF", 16) blue = rgb & int("0x000000FF", 16) alpha = (rgb>>24) & 0xff assert red == 0 and green == 0 and blue == 0 and alpha == 0 buffered_image = img_proc.add_text_to_image(buffered_image, "Hello world!", font, color, 10.0, 10.0, 10.0, 95, 30) #buffered_image = img_proc.add_text_to_image(buffered_image, "Franz ist hier!?", font, Color.BLUE,10.0, 2.0) # the left-outline of letter "H" in "Hello world" must NOW appear in the image! for i in range(0, 15): rgb = buffered_image.getRGB(15, i) red = rgb >> 16 & int("0x000000FF", 16) green = rgb >> 8 & int("0x000000FF", 16) blue = rgb & int("0x000000FF", 16) alpha = (rgb>>24) & 0xff if font_name == self._allowed_font_names[3]: #helvetica assert red == value[0] and green == value[1] and blue == value[2] and alpha == 255
def test_can_create_font(self): for font_name in self._allowed_font_names: for (bold, italic) in [(False, False), (False, True), (True, False), (True, True)]: font = img_proc.create_font(font_name, 14.0, ) assert isinstance(font, java.awt.Font) #print(font.getFontName() ) #results to error assert font.getSize() == 14
def convert(self, progress_bar = None): all_used_resources = [] unconverted_media_resources = [] converted_media_resources_paths = set() # TODO: remove this block later { for scratch_md5_name, src_path in self.scratch_project.md5_to_resource_path_map.iteritems(): if scratch_md5_name in self.scratch_project.unused_resource_names: log.info("Ignoring unused resource file: %s", src_path) if progress_bar != None: progress_bar.update(ProgressType.CONVERT_MEDIA_FILE) continue # } for scratch_object in self.scratch_project.objects: project_base_path = self.scratch_project.project_base_path for costume_info in scratch_object.get_costumes(): costume_file_name = costume_info[JsonKeys.COSTUME_MD5] costume_src_path = os.path.join(project_base_path, costume_file_name) file_ext = os.path.splitext(costume_file_name)[1].lower() if not os.path.exists(costume_src_path): # media files of local projects are NOT named by their hash-value -> change name costume_src_path = self.scratch_project.md5_to_resource_path_map[costume_file_name] assert os.path.exists(costume_src_path), "Not existing: {}".format(costume_src_path) assert file_ext in {".png", ".svg", ".jpg", ".gif"}, \ "Unsupported image file extension: %s" % costume_src_path ispng = file_ext == ".png" is_unconverted = file_ext == ".svg" resource_info = { "scratch_md5_name": costume_file_name, "src_path": costume_src_path, "dest_path": self.images_path, "media_type": MediaType.UNCONVERTED_SVG if is_unconverted else MediaType.IMAGE, "info": costume_info } all_used_resources.append(resource_info) if is_unconverted: unconverted_media_resources.append(resource_info) elif progress_bar != None and costume_src_path not in converted_media_resources_paths: # update progress bar for all those media files that don't have to be converted #TODO: background gets scaled too, shouldn't be the case if ispng: isStageCostume = scratch_object.name == "Stage" self.convertPNG(isStageCostume, costume_info,costume_src_path, costume_src_path) converted_media_resources_paths.add(costume_src_path) progress_bar.update(ProgressType.CONVERT_MEDIA_FILE) for sound_info in scratch_object.get_sounds(): sound_file_name = sound_info[JsonKeys.SOUND_MD5] sound_src_path = os.path.join(project_base_path, sound_file_name) file_ext = os.path.splitext(sound_file_name)[1].lower() if not os.path.exists(sound_src_path): # media files of local projects are NOT named by their hash-value -> change name sound_src_path = self.scratch_project.md5_to_resource_path_map[sound_file_name] assert os.path.exists(sound_src_path), "Not existing: {}".format(sound_src_path) assert file_ext in {".wav", ".mp3"}, "Unsupported sound file extension: %s" % sound_src_path is_unconverted = file_ext == ".wav" and not wavconverter.is_android_compatible_wav(sound_src_path) resource_info = { "scratch_md5_name": sound_file_name, "src_path": sound_src_path, "dest_path": self.sounds_path, "media_type": MediaType.UNCONVERTED_WAV if is_unconverted else MediaType.AUDIO, "info": sound_info } all_used_resources.append(resource_info) if is_unconverted: unconverted_media_resources.append(resource_info) elif progress_bar != None and sound_src_path not in converted_media_resources_paths: # update progress bar for all those media files that don't have to be converted progress_bar.update(ProgressType.CONVERT_MEDIA_FILE) converted_media_resources_paths.add(sound_src_path) # schedule concurrent conversions (one conversion per thread) new_src_paths = {} resource_index = 0 num_total_resources = len(unconverted_media_resources) reference_index = 0 media_srces = [] while resource_index < num_total_resources: num_next_resources = min(MAX_CONCURRENT_THREADS, (num_total_resources - resource_index)) next_resources_end_index = resource_index + num_next_resources threads = [] for index in range(resource_index, next_resources_end_index): assert index == reference_index reference_index += 1 data = unconverted_media_resources[index] if data["src_path"] in media_srces: continue else: media_srces.append(data["src_path"]) kwargs = { "data": data, "new_src_paths": new_src_paths, "progress_bar": progress_bar } threads.append(_MediaResourceConverterThread(kwargs=kwargs)) for thread in threads: thread.start() for thread in threads: thread.join() resource_index = next_resources_end_index assert reference_index == resource_index and reference_index == num_total_resources converted_media_files_to_be_removed = set() duplicate_filename_set = set() for resource_info in all_used_resources: # reconstruct the temporary catrobat filenames -> catrobat.media_objects_in(self.catrobat_file) current_filename = helpers.create_catrobat_md5_filename(resource_info["scratch_md5_name"], duplicate_filename_set) # check if path changed after conversion old_src_path = resource_info["src_path"] if old_src_path in new_src_paths: src_path = new_src_paths[old_src_path] else: src_path = old_src_path if resource_info["media_type"] in { MediaType.IMAGE, MediaType.UNCONVERTED_SVG }: costume_info = resource_info["info"] if "text" in costume_info: text = costume_info[JsonKeys.COSTUME_TEXT] x, y, width, height = costume_info[JsonKeys.COSTUME_TEXT_RECT] # TODO: extract RGBA # text_color = costume_info[JsonKeys.COSTUME_TEXT_COLOR] font_name = "NO FONT" font_style = "regular" font_scaling_factor = costume_info[JsonKeys.COSTUME_RESOLUTION] if JsonKeys.COSTUME_RESOLUTION in costume_info else 1 if len(costume_info[JsonKeys.COSTUME_FONT_NAME].split()) == 2 : [font_name, font_style] = costume_info[JsonKeys.COSTUME_FONT_NAME].split() else: log.warning("font JSON parameters wrong '{0}', replacing with known font '{1}'".format(costume_info[JsonKeys.COSTUME_FONT_NAME], image_processing._supported_fonts_path_mapping.keys()[0])) font_scaling_factor = font_scaling_factor * 1.1 # the original font might be smaller, better scale it down than cut it off if(font_name not in image_processing._supported_fonts_path_mapping): log.warning("font name '{0}' unknown, replacing with known font '{1}'".format(font_name, image_processing._supported_fonts_path_mapping.keys()[0])) font_name = image_processing._supported_fonts_path_mapping.keys()[0] font_scaling_factor = font_scaling_factor * 1.1 # the original font might be smaller, better scale it down than cut it off is_bold = font_style == "Bold" is_italic = font_style == "Italic" font_size = float(costume_info[JsonKeys.COSTUME_FONT_SIZE]) / float(font_scaling_factor) image_file_path = src_path font = image_processing.create_font(font_name, font_size, is_bold, is_italic) assert font is not None editable_image = image_processing.read_editable_image_from_disk(image_file_path) fonty = float(y) + (height * float(font_scaling_factor) / 2.0) # I think this might not work if we rotate something outside of the picture editable_image = image_processing.add_text_to_image(editable_image, text, font, Color.BLACK, float(x), float(fonty), float(width), float(height)) # TODO: create duplicate... # TODO: move test_converter.py to converter-python-package... image_processing.save_editable_image_as_png_to_disk(editable_image, image_file_path, overwrite=True) current_basename, _ = os.path.splitext(current_filename) self.file_rename_map[current_basename] = {} self.file_rename_map[current_basename]["src_path"] = src_path self.file_rename_map[current_basename]["dst_path"] = resource_info["dest_path"] self.file_rename_map[current_basename]["media_type"] = resource_info["media_type"] if resource_info["media_type"] in { MediaType.UNCONVERTED_SVG, MediaType.UNCONVERTED_WAV }: converted_media_files_to_be_removed.add(src_path) self.rename_media_files_and_copy() # delete converted png files -> only temporary saved for media_file_to_be_removed in converted_media_files_to_be_removed: os.remove(media_file_to_be_removed)
def convert(self, progress_bar=None): all_used_resources = [] unconverted_media_resources = [] converted_media_resources_paths = set() # TODO: remove this block later { for scratch_md5_name, src_path in self.scratch_project.md5_to_resource_path_map.iteritems( ): if scratch_md5_name in self.scratch_project.unused_resource_names: log.info("Ignoring unused resource file: %s", src_path) if progress_bar != None: progress_bar.update(ProgressType.CONVERT_MEDIA_FILE) continue # } for scratch_object in self.scratch_project.objects: project_base_path = self.scratch_project.project_base_path for costume_info in scratch_object.get_costumes(): costume_file_name = costume_info[JsonKeys.COSTUME_MD5] costume_src_path = os.path.join(project_base_path, costume_file_name) file_ext = os.path.splitext(costume_file_name)[1].lower() if not os.path.exists(costume_src_path): # media files of local projects are NOT named by their hash-value -> change name costume_src_path = self.scratch_project.md5_to_resource_path_map[ costume_file_name] assert os.path.exists( costume_src_path), "Not existing: {}".format( costume_src_path) assert file_ext in {".png", ".svg", ".jpg", ".gif"}, \ "Unsupported image file extension: %s" % costume_src_path is_unconverted = file_ext == ".svg" resource_info = { "scratch_md5_name": costume_file_name, "src_path": costume_src_path, "dest_path": self.images_path, "media_type": MediaType.UNCONVERTED_SVG if is_unconverted else MediaType.IMAGE, "info": costume_info } all_used_resources.append(resource_info) if is_unconverted: unconverted_media_resources.append(resource_info) elif progress_bar != None and costume_src_path not in converted_media_resources_paths: # update progress bar for all those media files that don't have to be converted progress_bar.update(ProgressType.CONVERT_MEDIA_FILE) converted_media_resources_paths.add(costume_src_path) for sound_info in scratch_object.get_sounds(): sound_file_name = sound_info[JsonKeys.SOUND_MD5] sound_src_path = os.path.join(project_base_path, sound_file_name) file_ext = os.path.splitext(sound_file_name)[1].lower() if not os.path.exists(sound_src_path): # media files of local projects are NOT named by their hash-value -> change name sound_src_path = self.scratch_project.md5_to_resource_path_map[ sound_file_name] assert os.path.exists( sound_src_path), "Not existing: {}".format(sound_src_path) assert file_ext in { ".wav", ".mp3" }, "Unsupported sound file extension: %s" % sound_src_path is_unconverted = file_ext == ".wav" and not wavconverter.is_android_compatible_wav( sound_src_path) resource_info = { "scratch_md5_name": sound_file_name, "src_path": sound_src_path, "dest_path": self.sounds_path, "media_type": MediaType.UNCONVERTED_WAV if is_unconverted else MediaType.AUDIO, "info": sound_info } all_used_resources.append(resource_info) if is_unconverted: unconverted_media_resources.append(resource_info) elif progress_bar != None and sound_src_path not in converted_media_resources_paths: # update progress bar for all those media files that don't have to be converted progress_bar.update(ProgressType.CONVERT_MEDIA_FILE) converted_media_resources_paths.add(sound_src_path) # schedule concurrent conversions (one conversion per thread) new_src_paths = {} resource_index = 0 num_total_resources = len(unconverted_media_resources) reference_index = 0 all_unconverted_src_media_paths = set() while resource_index < num_total_resources: num_next_resources = min(MAX_CONCURRENT_THREADS, (num_total_resources - resource_index)) next_resources_end_index = resource_index + num_next_resources threads = [] for index in range(resource_index, next_resources_end_index): assert index == reference_index reference_index += 1 data = unconverted_media_resources[index] should_update_progress_bar = not data[ "src_path"] in all_unconverted_src_media_paths all_unconverted_src_media_paths.add(data["src_path"]) kwargs = { "data": data, "new_src_paths": new_src_paths, "progress_bar": progress_bar if should_update_progress_bar else None } threads.append(_MediaResourceConverterThread(kwargs=kwargs)) for thread in threads: thread.start() for thread in threads: thread.join() resource_index = next_resources_end_index assert reference_index == resource_index and reference_index == num_total_resources converted_media_files_to_be_removed = set() for resource_info in all_used_resources: scratch_md5_name = resource_info["scratch_md5_name"] # check if path changed after conversion old_src_path = resource_info["src_path"] if old_src_path in new_src_paths: src_path = new_src_paths[old_src_path] else: src_path = old_src_path if resource_info["media_type"] in { MediaType.IMAGE, MediaType.UNCONVERTED_SVG }: costume_info = resource_info["info"] if "text" in costume_info: text = costume_info[JsonKeys.COSTUME_TEXT] x, y, width, height = costume_info[ JsonKeys.COSTUME_TEXT_RECT] # TODO: extract RGBA # text_color = costume_info[JsonKeys.COSTUME_TEXT_COLOR] [font_name, font_style ] = costume_info[JsonKeys.COSTUME_FONT_NAME].split() is_bold = font_style == "Bold" is_italic = font_style == "Italic" font_size = float(costume_info[JsonKeys.COSTUME_FONT_SIZE]) image_file_path = src_path font = image_processing.create_font( font_name, font_size, is_bold, is_italic) assert font is not None editable_image = image_processing.read_editable_image_from_disk( image_file_path) editable_image = image_processing.add_text_to_image( editable_image, text, font, Color.BLACK, float(x), float(y), float(width), float(height)) # TODO: create duplicate... # TODO: move test_converter.py to converter-python-package... image_processing.save_editable_image_as_png_to_disk( editable_image, image_file_path, overwrite=True) self._copy_media_file(scratch_md5_name, src_path, resource_info["dest_path"], resource_info["media_type"]) if resource_info["media_type"] in { MediaType.UNCONVERTED_SVG, MediaType.UNCONVERTED_WAV }: converted_media_files_to_be_removed.add(src_path) self._update_file_names_of_converted_media_files() for media_file_to_be_removed in converted_media_files_to_be_removed: os.remove(media_file_to_be_removed)
def convert(self, progress_bar = None): all_used_resources = [] unconverted_media_resources = [] converted_media_resources_paths = set() # TODO: remove this block later { for scratch_md5_name, src_path in self.scratch_project.md5_to_resource_path_map.iteritems(): if scratch_md5_name in self.scratch_project.unused_resource_names: log.info("Ignoring unused resource file: %s", src_path) if progress_bar != None: progress_bar.update(ProgressType.CONVERT_MEDIA_FILE) continue # } for scratch_object in self.scratch_project.objects: project_base_path = self.scratch_project.project_base_path for costume_info in scratch_object.get_costumes(): costume_file_name = costume_info[JsonKeys.COSTUME_MD5] costume_src_path = os.path.join(project_base_path, costume_file_name) file_ext = os.path.splitext(costume_file_name)[1].lower() if not os.path.exists(costume_src_path): # media files of local projects are NOT named by their hash-value -> change name costume_src_path = self.scratch_project.md5_to_resource_path_map[costume_file_name] assert os.path.exists(costume_src_path), "Not existing: {}".format(costume_src_path) assert file_ext in {".png", ".svg", ".jpg", ".gif"}, \ "Unsupported image file extension: %s" % costume_src_path ispng = file_ext == ".png" is_unconverted = file_ext == ".svg" resource_info = { "scratch_md5_name": costume_file_name, "src_path": costume_src_path, "dest_path": self.images_path, "media_type": MediaType.UNCONVERTED_SVG if is_unconverted else MediaType.IMAGE, "info": costume_info } all_used_resources.append(resource_info) if is_unconverted: unconverted_media_resources.append(resource_info) elif progress_bar != None and costume_src_path not in converted_media_resources_paths: # update progress bar for all those media files that don't have to be converted #TODO: background gets scaled too, shouldn't be the case if ispng: isStageCostume = scratch_object.name == "Stage" self.convertPNG(isStageCostume, costume_info,costume_src_path, costume_src_path) converted_media_resources_paths.add(costume_src_path) progress_bar.update(ProgressType.CONVERT_MEDIA_FILE) for sound_info in scratch_object.get_sounds(): sound_file_name = sound_info[JsonKeys.SOUND_MD5] sound_src_path = os.path.join(project_base_path, sound_file_name) file_ext = os.path.splitext(sound_file_name)[1].lower() if not os.path.exists(sound_src_path): # media files of local projects are NOT named by their hash-value -> change name sound_src_path = self.scratch_project.md5_to_resource_path_map[sound_file_name] assert os.path.exists(sound_src_path), "Not existing: {}".format(sound_src_path) assert file_ext in {".wav", ".mp3"}, "Unsupported sound file extension: %s" % sound_src_path is_unconverted = file_ext == ".wav" and not wavconverter.is_android_compatible_wav(sound_src_path) resource_info = { "scratch_md5_name": sound_file_name, "src_path": sound_src_path, "dest_path": self.sounds_path, "media_type": MediaType.UNCONVERTED_WAV if is_unconverted else MediaType.AUDIO, "info": sound_info } all_used_resources.append(resource_info) if is_unconverted: unconverted_media_resources.append(resource_info) elif progress_bar != None and sound_src_path not in converted_media_resources_paths: # update progress bar for all those media files that don't have to be converted progress_bar.update(ProgressType.CONVERT_MEDIA_FILE) converted_media_resources_paths.add(sound_src_path) # schedule concurrent conversions (one conversion per thread) new_src_paths = {} resource_index = 0 num_total_resources = len(unconverted_media_resources) reference_index = 0 media_srces = [] while resource_index < num_total_resources: num_next_resources = min(MAX_CONCURRENT_THREADS, (num_total_resources - resource_index)) next_resources_end_index = resource_index + num_next_resources threads = [] for index in range(resource_index, next_resources_end_index): assert index == reference_index reference_index += 1 data = unconverted_media_resources[index] if data["src_path"] in media_srces: continue else: media_srces.append(data["src_path"]) kwargs = { "data": data, "new_src_paths": new_src_paths, "progress_bar": progress_bar } threads.append(_MediaResourceConverterThread(kwargs=kwargs)) for thread in threads: thread.start() for thread in threads: thread.join() resource_index = next_resources_end_index assert reference_index == resource_index and reference_index == num_total_resources converted_media_files_to_be_removed = set() for resource_info in all_used_resources: scratch_md5_name = resource_info["scratch_md5_name"] # check if path changed after conversion old_src_path = resource_info["src_path"] if old_src_path in new_src_paths: src_path = new_src_paths[old_src_path] else: src_path = old_src_path if resource_info["media_type"] in { MediaType.IMAGE, MediaType.UNCONVERTED_SVG }: costume_info = resource_info["info"] if "text" in costume_info: text = costume_info[JsonKeys.COSTUME_TEXT] x, y, width, height = costume_info[JsonKeys.COSTUME_TEXT_RECT] # TODO: extract RGBA # text_color = costume_info[JsonKeys.COSTUME_TEXT_COLOR] font_name = "NO FONT" font_style = "regular" font_scaling_factor = costume_info[JsonKeys.COSTUME_RESOLUTION] if JsonKeys.COSTUME_RESOLUTION in costume_info else 1 if len(costume_info[JsonKeys.COSTUME_FONT_NAME].split()) == 2 : [font_name, font_style] = costume_info[JsonKeys.COSTUME_FONT_NAME].split() else: log.warning("font JSON parameters wrong '{0}', replacing with known font '{1}'".format(costume_info[JsonKeys.COSTUME_FONT_NAME], image_processing._supported_fonts_path_mapping.keys()[0])) font_scaling_factor = font_scaling_factor * 1.1 # the original font might be smaller, better scale it down than cut it off if(font_name not in image_processing._supported_fonts_path_mapping): log.warning("font name '{0}' unknown, replacing with known font '{1}'".format(font_name, image_processing._supported_fonts_path_mapping.keys()[0])) font_name = image_processing._supported_fonts_path_mapping.keys()[0] font_scaling_factor = font_scaling_factor * 1.1 # the original font might be smaller, better scale it down than cut it off is_bold = font_style == "Bold" is_italic = font_style == "Italic" font_size = float(costume_info[JsonKeys.COSTUME_FONT_SIZE]) / float(font_scaling_factor) image_file_path = src_path font = image_processing.create_font(font_name, font_size, is_bold, is_italic) assert font is not None editable_image = image_processing.read_editable_image_from_disk(image_file_path) fonty = float(y) + (height * float(font_scaling_factor) / 2.0) # I think this might not work if we rotate something outside of the picture editable_image = image_processing.add_text_to_image(editable_image, text, font, Color.BLACK, float(x), float(fonty), float(width), float(height)) # TODO: create duplicate... # TODO: move test_converter.py to converter-python-package... image_processing.save_editable_image_as_png_to_disk(editable_image, image_file_path, overwrite=True) self._copy_media_file(scratch_md5_name, src_path, resource_info["dest_path"], resource_info["media_type"]) if resource_info["media_type"] in { MediaType.UNCONVERTED_SVG, MediaType.UNCONVERTED_WAV }: converted_media_files_to_be_removed.add(src_path) self._update_file_names_of_converted_media_files() for media_file_to_be_removed in converted_media_files_to_be_removed: os.remove(media_file_to_be_removed)
def test_can_add_text_to_editable_image(self): dummy_png = self.img_proc_pngfile_paths()[0] #buffered_image = img_proc.read_editable_image_from_disk(dummy_png) #assert isinstance(buffered_image, java.awt.image.BufferedImage) for font_name in self._allowed_font_names: font = img_proc.create_font(font_name, 14.0, bold=False, italic=False) # check whether the left-outline of letter "H" in "Hello world" is NOT present in the image! test_colors = { Color.BLUE: (0, 0, 255), Color.RED: (255, 0, 0), Color.WHITE: (255, 255, 255), Color.BLACK: (0,0,0) } for (color, value) in test_colors.iteritems(): buffered_image = img_proc.read_editable_image_from_disk(dummy_png) assert isinstance(buffered_image, java.awt.image.BufferedImage) #test1 8 pixel down for i in range(0, 8): rgb = buffered_image.getRGB(11, i) red = rgb >> 16 & int("0x000000FF", 16) green = rgb >> 8 & int("0x000000FF", 16) blue = rgb & int("0x000000FF", 16) alpha = (rgb>>24) & 0xff assert red == 0 and green == 0 and blue == 0 and alpha == 0 buffered_image = img_proc.add_text_to_image(buffered_image, "Hello world!", font, color, 10.0, 10.0) #buffered_image = img_proc.add_text_to_image(buffered_image, "Franz ist hier!?", font, Color.BLUE,10.0, 2.0) # the left-outline of letter "H" in "Hello world" must NOW appear in the image! for i in range(0, 8): rgb = buffered_image.getRGB(11, i) red = rgb >> 16 & int("0x000000FF", 16) green = rgb >> 8 & int("0x000000FF", 16) blue = rgb & int("0x000000FF", 16) alpha = (rgb>>24) & 0xff; if font_name == self._allowed_font_names[3]: #helvetica assert red == value[0] and green == value[1] and blue == value[2] and alpha == 255 if font_name == self._allowed_font_names[0]: #marker if i > 0 and i < 8: assert red == value[0] and green == value[1] and blue == value[2] and alpha == 255 if font_name == self._allowed_font_names[1]: #scratch if i == 1 or i == 6 or i == 7: assert red == value[0] and green == value[1] and blue == value[2] and alpha == 255 if font_name == self._allowed_font_names[2]: #gloria if i == 6 or i == 7: assert red == value[0] and green == value[1] and blue == value[2] and alpha == 255 if font_name == self._allowed_font_names[4]: #donegal if i == 1: assert red == value[0] and green == value[1] and blue == value[2] and alpha == 255 if font_name == self._allowed_font_names[5]: #mystery assert red == value[0] and green == value[1] and blue == value[2] and alpha == 255 #test2 second letter 10 pixel down buffered_image = img_proc.read_editable_image_from_disk(dummy_png) assert isinstance(buffered_image, java.awt.image.BufferedImage) for i in range(20, 29): rgb = buffered_image.getRGB(i, 3) red = rgb >> 16 & int("0x000000FF", 16) green = rgb >> 8 & int("0x000000FF", 16) blue = rgb & int("0x000000FF", 16) alpha = (rgb>>24) & 0xff assert red == 0 and green == 0 and blue == 0 and alpha == 0 buffered_image = img_proc.add_text_to_image(buffered_image, "Hello world!", font, color, 10.0, 10.0) #buffered_image = img_proc.add_text_to_image(buffered_image, "Franz ist hier!?", font, Color.BLUE,10.0, 2.0) #some pixels in x space line must appear now for i in range(20, 29): rgb = buffered_image.getRGB(i, 3) red = rgb >> 16 & int("0x000000FF", 16) green = rgb >> 8 & int("0x000000FF", 16) blue = rgb & int("0x000000FF", 16) alpha = (rgb>>24) & 0xff; if font_name == self._allowed_font_names[3]: #helvetica if i == 22 or i == 23 or i == 24 or i == 25: assert red == value[0] and green == value[1] and blue == value[2] and alpha == 255 if font_name == self._allowed_font_names[0]: #marker if (i >= 21 and i <= 26) or i == 29 or i == 30: assert red == value[0] and green == value[1] and blue == value[2] and alpha == 255 if font_name == self._allowed_font_names[1]: #scratch if (i >= 17 and i <= 21): assert red == 0 and green == 0 and blue == 0 and alpha == 0 if font_name == self._allowed_font_names[2]: #gloria if i == 23 or i == 24 or i == 26: assert red == value[0] and green == value[1] and blue == value[2]and alpha == 255 if font_name == self._allowed_font_names[4]: #donegal if i >=21 and i <= 26: assert red == 0 and green == 0 and blue == 0 and alpha == 0 if font_name == self._allowed_font_names[5]: #mystery if i >=20 and i <= 22: assert red == value[0] and green == value[1] and blue == value[2] and alpha == 255 #test3 first whole line test buffered_image = img_proc.read_editable_image_from_disk(dummy_png) assert isinstance(buffered_image, java.awt.image.BufferedImage) for i in range(10, 94): rgb = buffered_image.getRGB(i, 0) red = rgb >> 16 & int("0x000000FF", 16) green = rgb >> 8 & int("0x000000FF", 16) blue = rgb & int("0x000000FF", 16) alpha = (rgb>>24) & 0xff assert red == 0 and green == 0 and blue == 0 and alpha == 0 buffered_image = img_proc.add_text_to_image(buffered_image, "Hello world!", font, color, 10.0, 10.0) #buffered_image = img_proc.add_text_to_image(buffered_image, "Franz ist hier!?", font, Color.BLUE,10.0, 2.0) #some pixels in x space line must appear now for i in range(10, 94): rgb = buffered_image.getRGB(i, 0) red = rgb >> 16 & int("0x000000FF", 16) green = rgb >> 8 & int("0x000000FF", 16) blue = rgb & int("0x000000FF", 16) alpha = (rgb>>24) & 0xff if font_name == self._allowed_font_names[3]: #helvetica if i == 11 or i == 18 or i == 29 or i == 32 or i == 70 or i == 78 or i == 82: assert red == value[0] and green == value[1] and blue == value[2] and alpha == 255 if font_name == self._allowed_font_names[0]: #marker if i == 12 or i == 17 or i == 18: assert red == value[0] and green == value[1] and blue == value[2] and alpha == 255 if font_name == self._allowed_font_names[1]: #scratch if i == 14 or i == 15 or i == 16 or i == 17: assert red ==value[0] and green == value[1] and blue == value[2] and alpha == 255 if font_name == self._allowed_font_names[2]: #gloria if i == 12 or i == 19 or i == 30 or i ==34 or i == 79 or i == 88 or i == 92 or i == 93: assert red == value[0] and green == value[1] and blue == value[2] and alpha == 255 if font_name == self._allowed_font_names[4]: #donegal if i== 26 or i == 27 or i == 29 or i == 30 or i == 64 or i == 65 or i == 72: assert red == value[0] and green == value[1] and blue == value[2] and alpha == 255 if font_name == self._allowed_font_names[5]: #mystery if (i >=10 and i <= 12) or (i >= 15 and i <= 17) or i == 25 or i == 28 or i == 66 or i == 73 or i == 76 or i == 77: assert red == value[0] and green == value[1] and blue == value[2] and alpha == 255
def convert(self, progress_bar=None): all_used_resources = [] unconverted_media_resources = [] converted_media_resources_paths = set() # TODO: remove this block later { for scratch_md5_name, src_path in self.scratch_project.md5_to_resource_path_map.iteritems(): if scratch_md5_name in self.scratch_project.unused_resource_names: log.info("Ignoring unused resource file: %s", src_path) if progress_bar != None: progress_bar.update(ProgressType.CONVERT_MEDIA_FILE) continue # } for scratch_object in self.scratch_project.objects: project_base_path = self.scratch_project.project_base_path for costume_info in scratch_object.get_costumes(): costume_file_name = costume_info[JsonKeys.COSTUME_MD5] costume_src_path = os.path.join(project_base_path, costume_file_name) file_ext = os.path.splitext(costume_file_name)[1].lower() if not os.path.exists(costume_src_path): # media files of local projects are NOT named by their hash-value -> change name costume_src_path = self.scratch_project.md5_to_resource_path_map[costume_file_name] assert os.path.exists(costume_src_path), "Not existing: {}".format(costume_src_path) assert file_ext in {".png", ".svg", ".jpg", ".gif"}, ( "Unsupported image file extension: %s" % costume_src_path ) is_unconverted = file_ext == ".svg" resource_info = { "scratch_md5_name": costume_file_name, "src_path": costume_src_path, "dest_path": self.images_path, "media_type": MediaType.UNCONVERTED_SVG if is_unconverted else MediaType.IMAGE, "info": costume_info, } all_used_resources.append(resource_info) if is_unconverted: unconverted_media_resources.append(resource_info) elif progress_bar != None and costume_src_path not in converted_media_resources_paths: # update progress bar for all those media files that don't have to be converted progress_bar.update(ProgressType.CONVERT_MEDIA_FILE) converted_media_resources_paths.add(costume_src_path) for sound_info in scratch_object.get_sounds(): sound_file_name = sound_info[JsonKeys.SOUND_MD5] sound_src_path = os.path.join(project_base_path, sound_file_name) file_ext = os.path.splitext(sound_file_name)[1].lower() if not os.path.exists(sound_src_path): # media files of local projects are NOT named by their hash-value -> change name sound_src_path = self.scratch_project.md5_to_resource_path_map[sound_file_name] assert os.path.exists(sound_src_path), "Not existing: {}".format(sound_src_path) assert file_ext in {".wav", ".mp3"}, "Unsupported sound file extension: %s" % sound_src_path is_unconverted = file_ext == ".wav" and not wavconverter.is_android_compatible_wav(sound_src_path) resource_info = { "scratch_md5_name": sound_file_name, "src_path": sound_src_path, "dest_path": self.sounds_path, "media_type": MediaType.UNCONVERTED_WAV if is_unconverted else MediaType.AUDIO, "info": sound_info, } all_used_resources.append(resource_info) if is_unconverted: unconverted_media_resources.append(resource_info) elif progress_bar != None and sound_src_path not in converted_media_resources_paths: # update progress bar for all those media files that don't have to be converted progress_bar.update(ProgressType.CONVERT_MEDIA_FILE) converted_media_resources_paths.add(sound_src_path) # schedule concurrent conversions (one conversion per thread) new_src_paths = {} resource_index = 0 num_total_resources = len(unconverted_media_resources) reference_index = 0 all_unconverted_src_media_paths = set() while resource_index < num_total_resources: num_next_resources = min(MAX_CONCURRENT_THREADS, (num_total_resources - resource_index)) next_resources_end_index = resource_index + num_next_resources threads = [] for index in range(resource_index, next_resources_end_index): assert index == reference_index reference_index += 1 data = unconverted_media_resources[index] should_update_progress_bar = not data["src_path"] in all_unconverted_src_media_paths all_unconverted_src_media_paths.add(data["src_path"]) kwargs = { "data": data, "new_src_paths": new_src_paths, "progress_bar": progress_bar if should_update_progress_bar else None, } threads.append(_MediaResourceConverterThread(kwargs=kwargs)) for thread in threads: thread.start() for thread in threads: thread.join() resource_index = next_resources_end_index assert reference_index == resource_index and reference_index == num_total_resources converted_media_files_to_be_removed = set() for resource_info in all_used_resources: scratch_md5_name = resource_info["scratch_md5_name"] # check if path changed after conversion old_src_path = resource_info["src_path"] if old_src_path in new_src_paths: src_path = new_src_paths[old_src_path] else: src_path = old_src_path if resource_info["media_type"] in {MediaType.IMAGE, MediaType.UNCONVERTED_SVG}: costume_info = resource_info["info"] if "text" in costume_info: text = costume_info[JsonKeys.COSTUME_TEXT] x, y, width, height = costume_info[JsonKeys.COSTUME_TEXT_RECT] # TODO: extract RGBA # text_color = costume_info[JsonKeys.COSTUME_TEXT_COLOR] [font_name, font_style] = costume_info[JsonKeys.COSTUME_FONT_NAME].split() is_bold = font_style == "Bold" is_italic = font_style == "Italic" font_size = float(costume_info[JsonKeys.COSTUME_FONT_SIZE]) image_file_path = src_path font = image_processing.create_font(font_name, font_size, is_bold, is_italic) assert font is not None editable_image = image_processing.read_editable_image_from_disk(image_file_path) editable_image = image_processing.add_text_to_image( editable_image, text, font, Color.BLACK, float(x), float(y), float(width), float(height) ) # TODO: create duplicate... # TODO: move test_converter.py to converter-python-package... image_processing.save_editable_image_as_png_to_disk(editable_image, image_file_path, overwrite=True) self._copy_media_file(scratch_md5_name, src_path, resource_info["dest_path"], resource_info["media_type"]) if resource_info["media_type"] in {MediaType.UNCONVERTED_SVG, MediaType.UNCONVERTED_WAV}: converted_media_files_to_be_removed.add(src_path) self._update_file_names_of_converted_media_files() for media_file_to_be_removed in converted_media_files_to_be_removed: os.remove(media_file_to_be_removed)