diff --git a/app/code/Magento/Catalog/Block/Product/View/Gallery.php b/app/code/Magento/Catalog/Block/Product/View/Gallery.php
index 0edeba7d807ac8e4c1be154d236bbff7c93a87b5..97909ffaa889f283d8a07c3f87be508f0c855c0a 100644
--- a/app/code/Magento/Catalog/Block/Product/View/Gallery.php
+++ b/app/code/Magento/Catalog/Block/Product/View/Gallery.php
@@ -63,15 +63,13 @@ class Gallery extends \Magento\Catalog\Block\Product\View\AbstractView
                 );
                 $image->setData(
                     'medium_image_url',
-                    $this->_imageHelper->init($product, 'product_page_image_medium')
-                        ->constrainOnly(true)->keepAspectRatio(true)->keepFrame(false)
+                    $this->_imageHelper->init($product, 'product_page_image_medium_no_frame')
                         ->setImageFile($image->getFile())
                         ->getUrl()
                 );
                 $image->setData(
                     'large_image_url',
-                    $this->_imageHelper->init($product, 'product_page_image_large')
-                        ->constrainOnly(true)->keepAspectRatio(true)->keepFrame(false)
+                    $this->_imageHelper->init($product, 'product_page_image_large_no_frame')
                         ->setImageFile($image->getFile())
                         ->getUrl()
                 );
diff --git a/app/code/Magento/Catalog/Helper/Image.php b/app/code/Magento/Catalog/Helper/Image.php
index eb5e7142d3094f5457fde615bc4987a8df912ab1..6f13e9077f4732af3714ae2af50ca804a22f0aa2 100644
--- a/app/code/Magento/Catalog/Helper/Image.php
+++ b/app/code/Magento/Catalog/Helper/Image.php
@@ -195,7 +195,6 @@ class Image extends AbstractHelper
     protected function setImageProperties()
     {
         $this->_getModel()->setDestinationSubdir($this->getType());
-
         $this->_getModel()->setWidth($this->getWidth());
         $this->_getModel()->setHeight($this->getHeight());
 
@@ -241,25 +240,25 @@ class Image extends AbstractHelper
     {
         $this->setWatermark(
             $this->scopeConfig->getValue(
-                "design/watermark/{$this->_getModel()->getDestinationSubdir()}_image",
+                "design/watermark/{$this->getType()}_image",
                 \Magento\Store\Model\ScopeInterface::SCOPE_STORE
             )
         );
         $this->setWatermarkImageOpacity(
             $this->scopeConfig->getValue(
-                "design/watermark/{$this->_getModel()->getDestinationSubdir()}_imageOpacity",
+                "design/watermark/{$this->getType()}_imageOpacity",
                 \Magento\Store\Model\ScopeInterface::SCOPE_STORE
             )
         );
         $this->setWatermarkPosition(
             $this->scopeConfig->getValue(
-                "design/watermark/{$this->_getModel()->getDestinationSubdir()}_position",
+                "design/watermark/{$this->getType()}_position",
                 \Magento\Store\Model\ScopeInterface::SCOPE_STORE
             )
         );
         $this->setWatermarkSize(
             $this->scopeConfig->getValue(
-                "design/watermark/{$this->_getModel()->getDestinationSubdir()}_size",
+                "design/watermark/{$this->getType()}_size",
                 \Magento\Store\Model\ScopeInterface::SCOPE_STORE
             )
         );
@@ -500,10 +499,7 @@ class Image extends AbstractHelper
     protected function isScheduledActionsAllowed()
     {
         $model = $this->_getModel();
-        if ($model->isBaseFilePlaceholder()
-            && $model->getNewFile() === true
-            || $model->isCached()
-        ) {
+        if ($model->isBaseFilePlaceholder() || $model->isCached()) {
             return false;
         }
         return true;
diff --git a/app/code/Magento/Catalog/Model/Product/Image.php b/app/code/Magento/Catalog/Model/Product/Image.php
index 34e1ad30ad434add33508bb63c7b8b1a78ddc9fb..769faa682f422b569dd6673df819f05d7371aeb8 100644
--- a/app/code/Magento/Catalog/Model/Product/Image.php
+++ b/app/code/Magento/Catalog/Model/Product/Image.php
@@ -12,8 +12,8 @@
 namespace Magento\Catalog\Model\Product;
 
 use Magento\Framework\App\Filesystem\DirectoryList;
+use Magento\Framework\App\ObjectManager;
 use Magento\Framework\Image as MagentoImage;
-use Magento\Store\Model\Store;
 
 /**
  * @SuppressWarnings(PHPMD.TooManyFields)
@@ -170,6 +170,21 @@ class Image extends \Magento\Framework\Model\AbstractModel
      */
     protected $_storeManager;
 
+    /**
+     * @var \Magento\Catalog\Model\View\Asset\ImageFactory
+     */
+    private $viewAssetImageFactory;
+
+    /**
+     * @var \Magento\Catalog\Model\View\Asset\PlaceholderFactory
+     */
+    private $viewAssetPlaceholderFactory;
+
+    /**
+     * @var \Magento\Framework\View\Asset\LocalInterface
+     */
+    private $imageAsset;
+
     /**
      * @param \Magento\Framework\Model\Context $context
      * @param \Magento\Framework\Registry $registry
@@ -207,7 +222,6 @@ class Image extends \Magento\Framework\Model\AbstractModel
         $this->_coreFileStorageDatabase = $coreFileStorageDatabase;
         parent::__construct($context, $registry, $resource, $resourceCollection, $data);
         $this->_mediaDirectory = $filesystem->getDirectoryWrite(DirectoryList::MEDIA);
-        $result = $this->_mediaDirectory->create($this->_catalogProductMediaConfig->getBaseMediaPath());
         $this->_imageFactory = $imageFactory;
         $this->_assetRepo = $assetRepo;
         $this->_viewFileSystem = $viewFileSystem;
@@ -450,85 +464,29 @@ class Image extends \Magento\Framework\Model\AbstractModel
      * @param string $file
      * @return $this
      * @throws \Exception
-     * @SuppressWarnings(PHPMD.CyclomaticComplexity)
-     * @SuppressWarnings(PHPMD.NPathComplexity)
      */
     public function setBaseFile($file)
     {
         $this->_isBaseFilePlaceholder = false;
 
-        if ($file && 0 !== strpos($file, '/', 0)) {
-            $file = '/' . $file;
-        }
-        $baseDir = $this->_catalogProductMediaConfig->getBaseMediaPath();
-
-        if ('/no_selection' == $file) {
-            $file = null;
-        }
-        if ($file) {
-            if (!$this->_fileExists($baseDir . $file) || !$this->_checkMemory($baseDir . $file)) {
-                $file = null;
-            }
-        }
-        if (!$file) {
+        $this->imageAsset = $this->getViewAssetImageFactory()->create(
+            [
+                'miscParams' => $this->getMiscParams(),
+                'filePath' => $file,
+            ]
+        );
+        if ($file == 'no_selection' || !$this->_fileExists($this->imageAsset->getSourceFile())
+            || !$this->_checkMemory($this->imageAsset->getSourceFile())
+        ) {
             $this->_isBaseFilePlaceholder = true;
-            // check if placeholder defined in config
-            $isConfigPlaceholder = $this->_scopeConfig->getValue(
-                "catalog/placeholder/{$this->getDestinationSubdir()}_placeholder",
-                \Magento\Store\Model\ScopeInterface::SCOPE_STORE
+            $this->imageAsset = $this->getViewAssetPlaceholderFactory()->create(
+                [
+                    'type' => $this->getDestinationSubdir(),
+                ]
             );
-            $configPlaceholder = '/placeholder/' . $isConfigPlaceholder;
-            if (!empty($isConfigPlaceholder) && $this->_fileExists($baseDir . $configPlaceholder)) {
-                $file = $configPlaceholder;
-            } else {
-                $this->_newFile = true;
-                return $this;
-            }
-        }
-
-        $baseFile = $baseDir . $file;
-
-        if (!$file || !$this->_mediaDirectory->isFile($baseFile)) {
-            throw new \Exception(__('We can\'t find the image file.'));
         }
 
-        $this->_baseFile = $baseFile;
-
-        // build new filename (most important params)
-        $path = [
-            $this->_catalogProductMediaConfig->getBaseMediaPath(),
-            'cache',
-            $this->getDestinationSubdir(),
-        ];
-        if (!empty($this->_width) || !empty($this->_height)) {
-            $path[] = "{$this->_width}x{$this->_height}";
-        }
-
-        // add misk params as a hash
-        $miscParams = [
-            ($this->_keepAspectRatio ? '' : 'non') . 'proportional',
-            ($this->_keepFrame ? '' : 'no') . 'frame',
-            ($this->_keepTransparency ? '' : 'no') . 'transparency',
-            ($this->_constrainOnly ? 'do' : 'not') . 'constrainonly',
-            $this->_rgbToString($this->_backgroundColor),
-            'angle' . $this->_angle,
-            'quality' . $this->_quality,
-        ];
-
-        // if has watermark add watermark params to hash
-        if ($this->getWatermarkFile()) {
-            $miscParams[] = $this->getWatermarkFile();
-            $miscParams[] = $this->getWatermarkImageOpacity();
-            $miscParams[] = $this->getWatermarkPosition();
-            $miscParams[] = $this->getWatermarkWidth();
-            $miscParams[] = $this->getWatermarkHeight();
-        }
-
-        $path[] = md5(implode('_', $miscParams));
-
-        // append prepared filename
-        $this->_newFile = implode('/', $path) . $file;
-        // the $file contains heading slash
+        $this->_baseFile = $this->imageAsset->getSourceFile();
 
         return $this;
     }
@@ -542,6 +500,7 @@ class Image extends \Magento\Framework\Model\AbstractModel
     }
 
     /**
+     * @deprecated
      * @return bool|string
      */
     public function getNewFile()
@@ -690,10 +649,10 @@ class Image extends \Magento\Framework\Model\AbstractModel
      */
     public function saveFile()
     {
-        if ($this->_isBaseFilePlaceholder && $this->_newFile === true) {
+        if ($this->_isBaseFilePlaceholder) {
             return $this;
         }
-        $filename = $this->_mediaDirectory->getAbsolutePath($this->getNewFile());
+        $filename = $this->getBaseFile() ? $this->imageAsset->getPath() : null;
         $this->getImageProcessor()->save($filename);
         $this->_coreFileStorageDatabase->saveFile($filename);
         return $this;
@@ -704,17 +663,7 @@ class Image extends \Magento\Framework\Model\AbstractModel
      */
     public function getUrl()
     {
-        if ($this->_newFile === true) {
-            $url = $this->_assetRepo->getUrl(
-                "Magento_Catalog::images/product/placeholder/{$this->getDestinationSubdir()}.jpg"
-            );
-        } else {
-            $url = $this->_storeManager->getStore()->getBaseUrl(
-                \Magento\Framework\UrlInterface::URL_TYPE_MEDIA
-            ) . $this->_newFile;
-        }
-
-        return $url;
+        return $this->imageAsset->getUrl();
     }
 
     /**
@@ -740,9 +689,7 @@ class Image extends \Magento\Framework\Model\AbstractModel
      */
     public function isCached()
     {
-        if (is_string($this->_newFile)) {
-            return $this->_fileExists($this->_newFile);
-        }
+        return file_exists($this->imageAsset->getPath());
     }
 
     /**
@@ -939,18 +886,72 @@ class Image extends \Magento\Framework\Model\AbstractModel
      */
     public function getResizedImageInfo()
     {
-        $fileInfo = null;
-        if ($this->_newFile === true) {
-            $asset = $this->_assetRepo->createAsset(
-                "Magento_Catalog::images/product/placeholder/{$this->getDestinationSubdir()}.jpg"
-            );
-            $img = $asset->getSourceFile();
-            $fileInfo = getimagesize($img);
+        if ($this->isBaseFilePlaceholder() == true) {
+            $image = $this->imageAsset->getSourceFile();
         } else {
-            if ($this->_mediaDirectory->isFile($this->_mediaDirectory->getAbsolutePath($this->_newFile))) {
-                $fileInfo = getimagesize($this->_mediaDirectory->getAbsolutePath($this->_newFile));
-            }
+            $image = $this->imageAsset->getPath();
         }
-        return $fileInfo;
+        return getimagesize($image);
+    }
+
+    /**
+     * @return \Magento\Catalog\Model\View\Asset\ImageFactory
+     */
+    private function getViewAssetImageFactory()
+    {
+        if ($this->viewAssetImageFactory == null) {
+            $this->viewAssetImageFactory = ObjectManager::getInstance()->get(
+                \Magento\Catalog\Model\View\Asset\ImageFactory::class
+            );
+        }
+
+        return $this->viewAssetImageFactory;
+    }
+
+    /**
+     * @return \Magento\Catalog\Model\View\Asset\PlaceholderFactory
+     */
+    private function getViewAssetPlaceholderFactory()
+    {
+        if ($this->viewAssetPlaceholderFactory == null) {
+            $this->viewAssetPlaceholderFactory = ObjectManager::getInstance()->get(
+                \Magento\Catalog\Model\View\Asset\PlaceholderFactory::class
+            );
+        }
+
+        return $this->viewAssetPlaceholderFactory;
+    }
+
+    /**
+     * Retrieve misc params based on all image attributes
+     *
+     * @return array
+     * @SuppressWarnings(PHPMD.NPathComplexity)
+     */
+    private function getMiscParams()
+    {
+        $miscParams = [
+            'image_type' => $this->getDestinationSubdir(),
+            'image_height' => $this->getHeight(),
+            'image_width' => $this->getWidth(),
+            'keep_aspect_ratio' => ($this->_keepAspectRatio ? '' : 'non') . 'proportional',
+            'keep_frame' => ($this->_keepFrame ? '' : 'no') . 'frame',
+            'keep_transparency' => ($this->_keepTransparency ? '' : 'no') . 'transparency',
+            'constrain_only' => ($this->_constrainOnly ? 'do' : 'not') . 'constrainonly',
+            'background' => $this->_rgbToString($this->_backgroundColor),
+            'angle' => $this->_angle,
+            'quality' => $this->_quality,
+        ];
+
+        // if has watermark add watermark params to hash
+        if ($this->getWatermarkFile()) {
+            $miscParams['watermark_file'] = $this->getWatermarkFile();
+            $miscParams['watermark_image_opacity'] = $this->getWatermarkImageOpacity();
+            $miscParams['watermark_position'] = $this->getWatermarkPosition();
+            $miscParams['watermark_width'] = $this->getWatermarkWidth();
+            $miscParams['watermark_height'] = $this->getWatermarkHeight();
+        }
+
+        return $miscParams;
     }
 }
diff --git a/app/code/Magento/Catalog/Model/View/Asset/Image.php b/app/code/Magento/Catalog/Model/View/Asset/Image.php
new file mode 100644
index 0000000000000000000000000000000000000000..31129d7d892d2a3737b4263beb127a37c6e0217b
--- /dev/null
+++ b/app/code/Magento/Catalog/Model/View/Asset/Image.php
@@ -0,0 +1,191 @@
+<?php
+/**
+ * Copyright © 2016 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+
+namespace Magento\Catalog\Model\View\Asset;
+
+use Magento\Catalog\Model\Product\Media\ConfigInterface;
+use Magento\Framework\Encryption\Encryptor;
+use Magento\Framework\Encryption\EncryptorInterface;
+use Magento\Framework\View\Asset\ContextInterface;
+use Magento\Framework\View\Asset\LocalInterface;
+
+/**
+ * A locally available image file asset that can be referred with a file path
+ *
+ * This class is a value object with lazy loading of some of its data (content, physical file path)
+ */
+class Image implements LocalInterface
+{
+    /**
+     * @var string
+     */
+    private $filePath;
+
+    /**
+     * @var string
+     */
+    private $contentType = 'image';
+
+    /**
+     * @var ContextInterface
+     */
+    private $context;
+
+    /**
+     * Misc image params depend on size, transparency, quality, watermark etc.
+     *
+     * @var array
+     */
+    private $miscParams;
+
+    /**
+     * @var ConfigInterface
+     */
+    private $mediaConfig;
+
+    /**
+     * @var EncryptorInterface
+     */
+    private $encryptor;
+
+    /**
+     * Image constructor.
+     *
+     * @param ConfigInterface $mediaConfig
+     * @param ContextInterface $context
+     * @param EncryptorInterface $encryptor
+     * @param string $filePath
+     * @param array $miscParams
+     */
+    public function __construct(
+        ConfigInterface $mediaConfig,
+        ContextInterface $context,
+        EncryptorInterface $encryptor,
+        $filePath,
+        array $miscParams = []
+    ) {
+        $this->mediaConfig = $mediaConfig;
+        $this->context = $context;
+        $this->filePath = $filePath;
+        $this->miscParams = $miscParams;
+        $this->encryptor = $encryptor;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getUrl()
+    {
+        return $this->context->getBaseUrl() . $this->getRelativePath(DIRECTORY_SEPARATOR);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getContentType()
+    {
+        return $this->contentType;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getPath()
+    {
+        return $this->getRelativePath($this->context->getPath());
+    }
+
+    /**
+     * Subroutine for building path
+     *
+     * @param string $path
+     * @param string $item
+     * @return string
+     */
+    private function join($path, $item)
+    {
+        return trim(
+            $path . ($item ? DIRECTORY_SEPARATOR . ltrim($item, DIRECTORY_SEPARATOR) : ''),
+            DIRECTORY_SEPARATOR
+        );
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getSourceFile()
+    {
+        return $this->mediaConfig->getBaseMediaPath()
+            . DIRECTORY_SEPARATOR . ltrim($this->filePath, DIRECTORY_SEPARATOR);
+    }
+
+    /**
+     * Get source content type
+     *
+     * @return string
+     */
+    public function getSourceContentType()
+    {
+        return $this->contentType;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getContent()
+    {
+        return null;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getFilePath()
+    {
+        return $this->filePath;
+    }
+
+    /**
+     * {@inheritdoc}
+     * @return ContextInterface
+     */
+    public function getContext()
+    {
+        return $this->context;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getModule()
+    {
+        return 'cache';
+    }
+
+    /**
+     * Retrieve part of path based on misc params
+     *
+     * @return string
+     */
+    private function getMiscPath()
+    {
+        return $this->encryptor->hash(implode('_', $this->miscParams), Encryptor::HASH_VERSION_MD5);
+    }
+
+    /**
+     * Generate relative path
+     *
+     * @param string $result
+     * @return string
+     */
+    private function getRelativePath($result)
+    {
+        $result = $this->join($result, $this->getModule());
+        $result = $this->join($result, $this->getMiscPath());
+        $result = $this->join($result, $this->getFilePath());
+        return DIRECTORY_SEPARATOR . $result;
+    }
+}
diff --git a/app/code/Magento/Catalog/Model/View/Asset/Image/Context.php b/app/code/Magento/Catalog/Model/View/Asset/Image/Context.php
new file mode 100644
index 0000000000000000000000000000000000000000..33f0adb70c94113f51321ecce40cdffdee1ef769
--- /dev/null
+++ b/app/code/Magento/Catalog/Model/View/Asset/Image/Context.php
@@ -0,0 +1,59 @@
+<?php
+/**
+ * Copyright © 2016 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+
+namespace Magento\Catalog\Model\View\Asset\Image;
+
+use Magento\Framework\App\Filesystem\DirectoryList;
+use Magento\Framework\View\Asset\ContextInterface;
+
+/**
+ * A basic path context for assets that includes a directory path
+ */
+class Context implements ContextInterface
+{
+    /**
+     * @var \Magento\Framework\Filesystem\Directory\WriteInterface
+     */
+    private $mediaDirectory;
+
+    /**
+     * @var \Magento\Catalog\Model\Product\Media\ConfigInterface
+     */
+    private $mediaConfig;
+
+    /**
+     * @var \Magento\Framework\Filesystem
+     */
+    private $filesystem;
+
+    /**
+     */
+    public function __construct(
+        \Magento\Catalog\Model\Product\Media\ConfigInterface $mediaConfig,
+        \Magento\Framework\Filesystem $filesystem
+    ) {
+        $this->mediaConfig = $mediaConfig;
+        $this->filesystem = $filesystem;
+        $this->mediaDirectory = $this->filesystem->getDirectoryWrite(DirectoryList::MEDIA);
+        $this->mediaDirectory->create($this->mediaConfig->getBaseMediaPath());
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getPath()
+    {
+        return $this->mediaDirectory->getAbsolutePath($this->mediaConfig->getBaseMediaPath());
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getBaseUrl()
+    {
+        return $this->mediaConfig->getBaseMediaUrl();
+    }
+}
diff --git a/app/code/Magento/Catalog/Model/View/Asset/Placeholder.php b/app/code/Magento/Catalog/Model/View/Asset/Placeholder.php
new file mode 100644
index 0000000000000000000000000000000000000000..fd7dcd1c4692ec76af97bdfb782e756345985d4b
--- /dev/null
+++ b/app/code/Magento/Catalog/Model/View/Asset/Placeholder.php
@@ -0,0 +1,181 @@
+<?php
+/**
+ * Copyright © 2016 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+
+namespace Magento\Catalog\Model\View\Asset;
+
+use Magento\Framework\App\Config\ScopeConfigInterface;
+use Magento\Framework\View\Asset\ContextInterface;
+use Magento\Framework\View\Asset\File\NotFoundException;
+use Magento\Framework\View\Asset\LocalInterface;
+use Magento\Framework\View\Asset\Repository;
+
+/**
+ * A locally available image placeholder file asset that can be referred with a file type
+ */
+class Placeholder implements LocalInterface
+{
+    /**
+     * Type of placeholder
+     *
+     * @var string
+     */
+    private $type;
+
+    /**
+     * Filevpath of placeholder
+     *
+     * @var string
+     */
+    private $filePath;
+
+    /**
+     * @var string
+     */
+    private $contentType = 'image';
+
+    /**
+     * @var ContextInterface
+     */
+    private $context;
+
+    /**
+     * @var Repository
+     */
+    private $assetRepo;
+
+    /**
+     * Core store config
+     *
+     * @var ScopeConfigInterface
+     */
+    private $scopeConfig;
+
+    /**
+     * Placeholder constructor.
+     *
+     * @param ContextInterface $context
+     * @param ScopeConfigInterface $scopeConfig
+     * @param Repository $assetRepo
+     * @param string $type
+     */
+    public function __construct(
+        ContextInterface $context,
+        ScopeConfigInterface $scopeConfig,
+        Repository $assetRepo,
+        $type
+    ) {
+        $this->context = $context;
+        $this->scopeConfig = $scopeConfig;
+        $this->assetRepo = $assetRepo;
+        $this->type = $type;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getUrl()
+    {
+        if ($this->getFilePath() !== null) {
+            $result = $this->context->getBaseUrl() . '/' . $this->getModule() . '/' . $this->getFilePath();
+        } else {
+            $result = $this->assetRepo->getUrl("Magento_Catalog::images/product/placeholder/{$this->type}.jpg");
+        }
+
+        return $result;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getContentType()
+    {
+        return $this->contentType;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getPath()
+    {
+        if ($this->getFilePath() !== null) {
+            $result = $this->getContext()->getPath()
+                . DIRECTORY_SEPARATOR . $this->getModule()
+                . DIRECTORY_SEPARATOR . $this->getFilePath();
+        } else {
+            $defaultPlaceholder = $this->assetRepo->createAsset(
+                "Magento_Catalog::images/product/placeholder/{$this->type}.jpg"
+            );
+            try {
+                $result = $defaultPlaceholder->getSourceFile();
+            } catch (NotFoundException $e) {
+                $result = null;
+            }
+        }
+
+        return $result;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getSourceFile()
+    {
+        return $this->getPath();
+    }
+
+    /**
+     * Get source content type
+     *
+     * @return string
+     */
+    public function getSourceContentType()
+    {
+        return $this->contentType;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getContent()
+    {
+        return null;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getFilePath()
+    {
+        if ($this->filePath !== null) {
+            return $this->filePath;
+        }
+        // check if placeholder defined in config
+        $isConfigPlaceholder = $this->scopeConfig->getValue(
+            "catalog/placeholder/{$this->type}_placeholder",
+            \Magento\Store\Model\ScopeInterface::SCOPE_STORE
+        );
+        $this->filePath = $isConfigPlaceholder;
+
+        return $this->filePath;
+    }
+
+    /**
+     * {@inheritdoc}
+     * @return ContextInterface
+     */
+    public function getContext()
+    {
+        return $this->context;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getModule()
+    {
+        return 'placeholder';
+    }
+}
diff --git a/app/code/Magento/Catalog/Test/Unit/Block/Product/View/GalleryTest.php b/app/code/Magento/Catalog/Test/Unit/Block/Product/View/GalleryTest.php
index 2e3a896d956d9cdd1dd82ccee58873bcf6131460..3aee622b5e3b3a48910318157a33faf8498cfd37 100644
--- a/app/code/Magento/Catalog/Test/Unit/Block/Product/View/GalleryTest.php
+++ b/app/code/Magento/Catalog/Test/Unit/Block/Product/View/GalleryTest.php
@@ -111,8 +111,8 @@ class GalleryTest extends \PHPUnit_Framework_TestCase
             ->method('init')
             ->willReturnMap([
                 [$productMock, 'product_page_image_small', [], $this->imageHelper],
-                [$productMock, 'product_page_image_medium', [], $this->imageHelper],
-                [$productMock, 'product_page_image_large', [], $this->imageHelper],
+                [$productMock, 'product_page_image_medium_no_frame', [], $this->imageHelper],
+                [$productMock, 'product_page_image_large_no_frame', [], $this->imageHelper],
             ])
             ->willReturnSelf();
         $this->imageHelper->expects($this->exactly(3))
@@ -129,19 +129,6 @@ class GalleryTest extends \PHPUnit_Framework_TestCase
             ->method('getUrl')
             ->willReturn('product_page_image_large_url');
 
-        $this->imageHelper->expects($this->exactly(2))
-            ->method('constrainOnly')
-            ->with(true)
-            ->willReturnSelf();
-        $this->imageHelper->expects($this->exactly(2))
-            ->method('keepAspectRatio')
-            ->with(true)
-            ->willReturnSelf();
-         $this->imageHelper->expects($this->exactly(2))
-            ->method('keepFrame')
-            ->with(false)
-            ->willReturnSelf();
-
         $images = $this->model->getGalleryImages();
         $this->assertInstanceOf(\Magento\Framework\Data\Collection::class, $images);
     }
diff --git a/app/code/Magento/Catalog/Test/Unit/Helper/ImageTest.php b/app/code/Magento/Catalog/Test/Unit/Helper/ImageTest.php
index 206f20bc98179db80af5fb45014b061149dec8d7..4ed3495f9e34812788baf408632e5af29d521044 100644
--- a/app/code/Magento/Catalog/Test/Unit/Helper/ImageTest.php
+++ b/app/code/Magento/Catalog/Test/Unit/Helper/ImageTest.php
@@ -424,7 +424,6 @@ class ImageTest extends \PHPUnit_Framework_TestCase
      * @param string $imageId
      * @param string $imageFile
      * @param string $baseFile
-     * @param string $newFile
      * @param string $destination
      * @param boolean $setImageFile
      * @param boolean $isCached
@@ -436,7 +435,6 @@ class ImageTest extends \PHPUnit_Framework_TestCase
         $imageId,
         $imageFile,
         $baseFile,
-        $newFile,
         $destination,
         $setImageFile,
         $isCached,
@@ -477,9 +475,6 @@ class ImageTest extends \PHPUnit_Framework_TestCase
         $this->image->expects($this->any())
             ->method('isBaseFilePlaceholder')
             ->willReturn($isBaseFilePlaceholder);
-        $this->image->expects($this->any())
-            ->method('getNewFile')
-            ->willReturn($newFile);
 
         $this->prepareAttributes([], $imageId);
 
@@ -502,7 +497,6 @@ class ImageTest extends \PHPUnit_Framework_TestCase
                 'image_id' => 'test_image_id',
                 'image_file' => '/path/to/test_image_id.png',
                 'base_file' => '/path/to/base_image.png',
-                'new_file' => '/path/to/base_image.png',
                 'destination' => 'small_image',
                 'set_image_file' => true,
                 'is_cached' => false,
@@ -516,7 +510,6 @@ class ImageTest extends \PHPUnit_Framework_TestCase
                 'image_id' => 'test_image_id',
                 'image_file' => '/path/to/test_image_id.png',
                 'base_file' => null,
-                'new_file' => true,
                 'destination' => 'small_image',
                 'set_image_file' => false,
                 'is_cached' => false,
@@ -530,7 +523,6 @@ class ImageTest extends \PHPUnit_Framework_TestCase
                 'image_id' => 'test_image_id',
                 'image_file' => '/path/to/test_image_id.png',
                 'base_file' => null,
-                'new_file' => false,
                 'destination' => 'small_image',
                 'set_image_file' => true,
                 'is_cached' => false,
@@ -544,7 +536,6 @@ class ImageTest extends \PHPUnit_Framework_TestCase
                 'image_id' => 'test_image_id',
                 'image_file' => '/path/to/test_image_id.png',
                 'base_file' => null,
-                'new_file' => true,
                 'destination' => 'small_image',
                 'set_image_file' => true,
                 'is_cached' => false,
@@ -558,7 +549,6 @@ class ImageTest extends \PHPUnit_Framework_TestCase
                 'image_id' => 'test_image_id',
                 'image_file' => '/path/to/test_image_id.png',
                 'base_file' => null,
-                'new_file' => '/path/to/test_image_id.png',
                 'destination' => 'small_image',
                 'set_image_file' => true,
                 'is_cached' => false,
diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Product/ImageTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Product/ImageTest.php
index 44f7f87cc2c62afc2c09d262bbd2721683fcfea3..8ee875dad17b1c8fb09a99449c039825f9e1344c 100644
--- a/app/code/Magento/Catalog/Test/Unit/Model/Product/ImageTest.php
+++ b/app/code/Magento/Catalog/Test/Unit/Model/Product/ImageTest.php
@@ -6,12 +6,17 @@
 
 namespace Magento\Catalog\Test\Unit\Model\Product;
 
+use Magento\Catalog\Model\View\Asset\Image\ContextFactory;
+use Magento\Catalog\Model\View\Asset\ImageFactory;
+use Magento\Catalog\Model\View\Asset\PlaceholderFactory;
 use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper;
 use Magento\Framework\App\Filesystem\DirectoryList;
+use Magento\Framework\View\Asset\ContextInterface;
 
 /**
  * Class ImageTest
  * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
+ * @SuppressWarnings(PHPMD.TooManyFields)
  */
 class ImageTest extends \PHPUnit_Framework_TestCase
 {
@@ -75,6 +80,21 @@ class ImageTest extends \PHPUnit_Framework_TestCase
      */
     protected $mediaDirectory;
 
+    /**
+     * @var \Magento\Framework\View\Asset\LocalInterface|\PHPUnit_Framework_MockObject_MockObject
+     */
+    protected $imageAsset;
+
+    /**
+     * @var ImageFactory|\PHPUnit_Framework_MockObject_MockObject
+     */
+    protected $viewAssetImageFactory;
+
+    /**
+     * @var PlaceholderFactory|\PHPUnit_Framework_MockObject_MockObject
+     */
+    protected $viewAssetPlaceholderFactory;
+
     protected function setUp()
     {
         $this->context = $this->getMock(\Magento\Framework\Model\Context::class, [], [], '', false);
@@ -99,7 +119,6 @@ class ImageTest extends \PHPUnit_Framework_TestCase
             ->disableOriginalConstructor()
             ->setMethods(['create', 'isFile', 'isExist', 'getAbsolutePath'])
             ->getMock();
-        $this->mediaDirectory->expects($this->once())->method('create')->will($this->returnValue(true));
 
         $this->filesystem = $this->getMock(\Magento\Framework\Filesystem::class, [], [], '', false);
         $this->filesystem->expects($this->once())->method('getDirectoryWrite')
@@ -110,20 +129,49 @@ class ImageTest extends \PHPUnit_Framework_TestCase
         $this->fileSystem = $this->getMock(\Magento\Framework\View\FileSystem::class, [], [], '', false);
         $this->scopeConfigInterface = $this->getMock(\Magento\Framework\App\Config\ScopeConfigInterface::class);
 
+        $context = $this->getMockBuilder(\Magento\Framework\Model\Context::class)
+            ->disableOriginalConstructor()
+            ->getMock();
+        $this->image = new \Magento\Catalog\Model\Product\Image(
+            $context,
+            $this->registry,
+            $this->storeManager,
+            $this->config,
+            $this->coreFileHelper,
+            $this->filesystem,
+            $this->factory,
+            $this->repository,
+            $this->fileSystem,
+            $this->scopeConfigInterface
+        );
+        //Settings for backward compatible property
         $objectManagerHelper = new ObjectManagerHelper($this);
-        $this->image = $objectManagerHelper->getObject(
-            \Magento\Catalog\Model\Product\Image::class,
-            [
-                'registry' => $this->registry,
-                'storeManager' => $this->storeManager,
-                'catalogProductMediaConfig' => $this->config,
-                'coreFileStorageDatabase' => $this->coreFileHelper,
-                'filesystem' => $this->filesystem,
-                'imageFactory' => $this->factory,
-                'assetRepo' => $this->repository,
-                'viewFileSystem' => $this->fileSystem,
-                'scopeConfig' => $this->scopeConfigInterface
-            ]
+        $this->imageAsset = $this->getMockBuilder(\Magento\Framework\View\Asset\LocalInterface::class)
+            ->getMockForAbstractClass();
+        $objectManagerHelper->setBackwardCompatibleProperty(
+            $this->image,
+            'imageAsset',
+            $this->imageAsset
+        );
+
+        $this->viewAssetImageFactory = $this->getMockBuilder(ImageFactory::class)
+            ->disableOriginalConstructor()
+            ->setMethods(['create'])
+            ->getMock();
+        $objectManagerHelper->setBackwardCompatibleProperty(
+            $this->image,
+            'viewAssetImageFactory',
+            $this->viewAssetImageFactory
+        );
+
+        $this->viewAssetPlaceholderFactory = $this->getMockBuilder(PlaceholderFactory::class)
+            ->disableOriginalConstructor()
+            ->setMethods(['create'])
+            ->getMock();
+        $objectManagerHelper->setBackwardCompatibleProperty(
+            $this->image,
+            'viewAssetPlaceholderFactory',
+            $this->viewAssetPlaceholderFactory
         );
     }
 
@@ -177,18 +225,39 @@ class ImageTest extends \PHPUnit_Framework_TestCase
         $absolutePath = dirname(dirname(__DIR__)) . '/_files/catalog/product/somefile.png';
         $this->mediaDirectory->expects($this->any())->method('getAbsolutePath')
             ->will($this->returnValue($absolutePath));
+        $this->viewAssetImageFactory->expects($this->any())
+            ->method('create')
+            ->with(
+                [
+                    'miscParams' => [
+                        'image_type' => null,
+                        'image_height' => null,
+                        'image_width' => null,
+                        'keep_aspect_ratio' => 'proportional',
+                        'keep_frame' => 'frame',
+                        'keep_transparency' => 'transparency',
+                        'constrain_only' => 'doconstrainonly',
+                        'background' => 'ffffff',
+                        'angle' => null,
+                        'quality' => 80,
+                    ],
+                    'filePath' => '/somefile.png',
+                ]
+            )
+            ->willReturn($this->imageAsset);
+        $this->viewAssetPlaceholderFactory->expects($this->never())->method('create');
+
+        $this->imageAsset->expects($this->any())->method('getSourceFile')->willReturn('catalog/product/somefile.png');
         $this->image->setBaseFile('/somefile.png');
         $this->assertEquals('catalog/product/somefile.png', $this->image->getBaseFile());
-        $this->assertEquals(
-            'catalog/product/cache//beff4985b56e3afdbeabfc89641a4582/somefile.png',
-            $this->image->getNewFile()
-        );
     }
 
     public function testSetBaseNoSelectionFile()
     {
-        $this->image->setBaseFile('/no_selection');
-        $this->assertTrue($this->image->getNewFile());
+        $this->viewAssetPlaceholderFactory->expects($this->once())->method('create')->willReturn($this->imageAsset);
+        $this->imageAsset->expects($this->any())->method('getSourceFile')->willReturn('Default Placeholder Path');
+        $this->image->setBaseFile('no_selection');
+        $this->assertEquals('Default Placeholder Path', $this->image->getBaseFile());
     }
 
     public function testSetGetImageProcessor()
@@ -284,45 +353,45 @@ class ImageTest extends \PHPUnit_Framework_TestCase
         )->disableOriginalConstructor()->getMock();
         $this->image->setImageProcessor($imageProcessor);
         $this->coreFileHelper->expects($this->once())->method('saveFile')->will($this->returnValue(true));
-        $absolutePath = dirname(dirname(__DIR__)) . '/_files/catalog/product/somefile.png';
-        $this->mediaDirectory->expects($this->once())->method('getAbsolutePath')
-            ->will($this->returnValue($absolutePath));
 
         $this->image->saveFile();
     }
 
     public function testSaveFileNoSelection()
     {
-        $this->testSetBaseNoSelectionFile();
+        $imageProcessor = $this->getMockBuilder(
+            \Magento\Framework\Image::class
+        )->disableOriginalConstructor()->getMock();
+        $this->image->setImageProcessor($imageProcessor);
         $this->assertSame($this->image, $this->image->saveFile());
     }
 
     public function testGetUrl()
     {
         $this->testSetGetBaseFile();
-        $url = $this->image->getUrl();
-        $this->assertEquals(
-            'http://magento.com/media/catalog/product/cache//beff4985b56e3afdbeabfc89641a4582/somefile.png',
-            $url
-        );
+        $this->imageAsset->expects($this->any())->method('getUrl')->will($this->returnValue('url of exist image'));
+        $this->assertEquals('url of exist image', $this->image->getUrl());
     }
 
     public function testGetUrlNoSelection()
     {
-        $this->testSetBaseNoSelectionFile();
-        $this->repository->expects($this->once())->method('getUrl')->will($this->returnValue('someurl'));
-        $this->assertEquals('someurl', $this->image->getUrl());
+        $this->viewAssetPlaceholderFactory->expects($this->once())->method('create')->willReturn($this->imageAsset);
+        $this->imageAsset->expects($this->any())->method('getUrl')->will($this->returnValue('Default Placeholder URL'));
+        $this->image->setBaseFile('no_selection');
+        $this->assertEquals('Default Placeholder URL', $this->image->getUrl());
     }
 
     public function testSetGetDestinationSubdir()
     {
-        $this->image->setDestinationSubdir('somesubdir');
-        $this->assertEquals('somesubdir', $this->image->getDestinationSubdir());
+        $this->image->setDestinationSubdir('image_type');
+        $this->assertEquals('image_type', $this->image->getDestinationSubdir());
     }
 
     public function testIsCached()
     {
         $this->testSetGetBaseFile();
+        $absolutePath = dirname(dirname(__DIR__)) . '/_files/catalog/product/watermark/somefile.png';
+        $this->imageAsset->expects($this->any())->method('getPath')->willReturn($absolutePath);
         $this->assertTrue($this->image->isCached());
     }
 
diff --git a/app/code/Magento/Catalog/Test/Unit/Model/View/Asset/Image/ContextTest.php b/app/code/Magento/Catalog/Test/Unit/Model/View/Asset/Image/ContextTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..cdc1296486eef202a6410b6a3fd3a1d738ca60f9
--- /dev/null
+++ b/app/code/Magento/Catalog/Test/Unit/Model/View/Asset/Image/ContextTest.php
@@ -0,0 +1,76 @@
+<?php
+/**
+ * Copyright © 2016 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+namespace Magento\Catalog\Test\Unit\Model\View\Asset\Image;
+
+use Magento\Catalog\Model\Product\Media\ConfigInterface;
+use Magento\Catalog\Model\View\Asset\Image\Context;
+use Magento\Framework\App\Filesystem\DirectoryList;
+use Magento\Framework\Filesystem;
+use Magento\Framework\Filesystem\Directory\WriteInterface;
+
+/**
+ * Class ContextTest
+ */
+class ContextTest extends \PHPUnit_Framework_TestCase
+{
+    /**
+     * @var Context
+     */
+    protected $model;
+
+    /**
+     * @var WriteInterface|\PHPUnit_Framework_MockObject_MockObject
+     */
+    protected $mediaDirectory;
+
+    /**
+     * @var ContextInterface|\PHPUnit_Framework_MockObject_MockObject
+     */
+    protected $mediaConfig;
+
+    /**
+     * @var Filesystem|\PHPUnit_Framework_MockObject_MockObject
+     */
+    protected $filesystem;
+
+    protected function setUp()
+    {
+        $this->mediaConfig = $this->getMockBuilder(ConfigInterface::class)->getMockForAbstractClass();
+        $this->mediaConfig->expects($this->any())->method('getBaseMediaPath')->willReturn('catalog/product');
+        $this->mediaDirectory = $this->getMockBuilder(WriteInterface::class)->getMockForAbstractClass();
+        $this->mediaDirectory->expects($this->once())->method('create')->with('catalog/product');
+        $this->filesystem = $this->getMockBuilder(Filesystem::class)
+            ->disableOriginalConstructor()
+            ->getMock();
+        $this->filesystem->expects($this->once())
+            ->method('getDirectoryWrite')
+            ->with(DirectoryList::MEDIA)
+            ->willReturn($this->mediaDirectory);
+        $this->model = new Context(
+            $this->mediaConfig,
+            $this->filesystem
+        );
+    }
+
+    public function testGetPath()
+    {
+        $path = '/var/www/html/magento2ce/pub/media/catalog/product';
+        $this->mediaDirectory->expects($this->once())
+            ->method('getAbsolutePath')
+            ->with('catalog/product')
+            ->willReturn($path);
+
+        $this->assertEquals($path, $this->model->getPath());
+    }
+
+    public function testGetUrl()
+    {
+        $baseUrl = 'http://localhost/pub/media/catalog/product';
+        $this->mediaConfig->expects($this->once())->method('getBaseMediaUrl')->willReturn($baseUrl);
+
+        $this->assertEquals($baseUrl, $this->model->getBaseUrl());
+    }
+}
diff --git a/app/code/Magento/Catalog/Test/Unit/Model/View/Asset/ImageTest.php b/app/code/Magento/Catalog/Test/Unit/Model/View/Asset/ImageTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..5e96cdb1c3395182049e78ae29cd3b44bd0baf17
--- /dev/null
+++ b/app/code/Magento/Catalog/Test/Unit/Model/View/Asset/ImageTest.php
@@ -0,0 +1,148 @@
+<?php
+/**
+ * Copyright © 2016 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+namespace Magento\Catalog\Test\Unit\Model\View\Asset;
+
+use Magento\Catalog\Model\Product\Media\ConfigInterface;
+use Magento\Catalog\Model\View\Asset\Image;
+use Magento\Framework\Encryption\EncryptorInterface;
+use Magento\Framework\View\Asset\ContextInterface;
+
+/**
+ * Class ImageTest
+ */
+class ImageTest extends \PHPUnit_Framework_TestCase
+{
+    /**
+     * @var \Magento\Catalog\Model\View\Asset\Image
+     */
+    protected $model;
+
+    /**
+     * @var ContextInterface|\PHPUnit_Framework_MockObject_MockObject
+     */
+    protected $mediaConfig;
+
+    /**
+     * @var EncryptorInterface|\PHPUnit_Framework_MockObject_MockObject
+     */
+    protected $encryptor;
+
+    /**
+     * @var ContextInterface|\PHPUnit_Framework_MockObject_MockObject
+     */
+    protected $imageContext;
+
+    protected function setUp()
+    {
+        $this->mediaConfig = $this->getMockBuilder(ConfigInterface::class)->getMockForAbstractClass();
+        $this->encryptor = $this->getMockBuilder(EncryptorInterface::class)->getMockForAbstractClass();
+        $this->imageContext = $this->getMockBuilder(ContextInterface::class)->getMockForAbstractClass();
+        $this->model = new Image(
+            $this->mediaConfig,
+            $this->imageContext,
+            $this->encryptor,
+            '/somefile.png'
+        );
+    }
+
+    public function testModuleAndContentAndContentType()
+    {
+        $contentType = 'image';
+        $this->assertEquals($contentType, $this->model->getContentType());
+        $this->assertEquals($contentType, $this->model->getSourceContentType());
+        $this->assertNull($this->model->getContent());
+        $this->assertEquals('cache', $this->model->getModule());
+    }
+
+    public function testGetFilePath()
+    {
+        $this->assertEquals('/somefile.png', $this->model->getFilePath());
+    }
+
+    public function testGetSoureFile()
+    {
+        $this->mediaConfig->expects($this->once())->method('getBaseMediaPath')->willReturn('catalog/product');
+        $this->assertEquals('catalog/product/somefile.png', $this->model->getSourceFile());
+    }
+
+    public function testGetContext()
+    {
+        $this->assertInstanceOf(ContextInterface::class, $this->model->getContext());
+    }
+
+    /**
+     * @param string $filePath
+     * @param array $miscParams
+     * @dataProvider getPathDataProvider
+     */
+    public function testGetPath($filePath, $miscParams)
+    {
+        $imageModel = new Image(
+            $this->mediaConfig,
+            $this->imageContext,
+            $this->encryptor,
+            $filePath,
+            $miscParams
+        );
+        $absolutePath = '/var/www/html/magento2ce/pub/media/catalog/product';
+        $hashPath = md5(implode('_', $miscParams));
+        $this->imageContext->expects($this->once())->method('getPath')->willReturn($absolutePath);
+        $this->encryptor->expects($this->once())->method('hash')->willReturn($hashPath);
+        $this->assertEquals(
+            $absolutePath . DIRECTORY_SEPARATOR . 'cache' . DIRECTORY_SEPARATOR . $hashPath . $filePath,
+            $imageModel->getPath()
+        );
+    }
+
+    /**
+     * @param string $filePath
+     * @param array $miscParams
+     * @dataProvider getPathDataProvider
+     */
+    public function testGetUrl($filePath, $miscParams)
+    {
+        $imageModel = new Image(
+            $this->mediaConfig,
+            $this->imageContext,
+            $this->encryptor,
+            $filePath,
+            $miscParams
+        );
+        $absolutePath = 'http://localhost/pub/media/catalog/product';
+        $hashPath = md5(implode('_', $miscParams));
+        $this->imageContext->expects($this->once())->method('getBaseUrl')->willReturn($absolutePath);
+        $this->encryptor->expects($this->once())->method('hash')->willReturn($hashPath);
+        $this->assertEquals(
+            $absolutePath . DIRECTORY_SEPARATOR . 'cache' . DIRECTORY_SEPARATOR . $hashPath . $filePath,
+            $imageModel->getUrl()
+        );
+    }
+
+    public function getPathDataProvider()
+    {
+        return [
+            [
+                '/some_file.png',
+                [], //default value for miscParams
+            ],
+            [
+                '/some_file_2.png',
+                [
+                    'image_type' => 'thumbnail',
+                    'image_height' => 75,
+                    'image_width' => 75,
+                    'keep_aspect_ratio' => 'proportional',
+                    'keep_frame' => 'frame',
+                    'keep_transparency' => 'transparency',
+                    'constrain_only' => 'doconstrainonly',
+                    'background' => 'ffffff',
+                    'angle' => null,
+                    'quality' => 80,
+                ],
+            ]
+        ];
+    }
+}
diff --git a/app/code/Magento/Catalog/Test/Unit/Model/View/Asset/PlaceholderTest.php b/app/code/Magento/Catalog/Test/Unit/Model/View/Asset/PlaceholderTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..38d5ceb16e6b5b17e8e7cbaa1171513d68027e3c
--- /dev/null
+++ b/app/code/Magento/Catalog/Test/Unit/Model/View/Asset/PlaceholderTest.php
@@ -0,0 +1,163 @@
+<?php
+/**
+ * Copyright © 2016 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+namespace Magento\Catalog\Test\Unit\Model\View\Asset;
+
+use Magento\Framework\App\Config\ScopeConfigInterface;
+use Magento\Catalog\Model\View\Asset\Placeholder;
+use Magento\Framework\View\Asset\ContextInterface;
+use Magento\Framework\View\Asset\Repository;
+
+/**
+ * Class PlaceholderTest
+ */
+class PlaceholderTest extends \PHPUnit_Framework_TestCase
+{
+    /**
+     * @var \Magento\Catalog\Model\View\Asset\Placeholder
+     */
+    protected $model;
+
+    /**
+     * @var ScopeConfigInterface|\PHPUnit_Framework_MockObject_MockObject
+     */
+    protected $scopeConfig;
+
+    /**
+     * @var Repository|\PHPUnit_Framework_MockObject_MockObject
+     */
+    protected $repository;
+
+    /**
+     * @var ContextInterface|\PHPUnit_Framework_MockObject_MockObject
+     */
+    protected $imageContext;
+
+    protected function setUp()
+    {
+        $this->scopeConfig = $this->getMockBuilder(ScopeConfigInterface::class)->getMockForAbstractClass();
+        $this->imageContext = $this->getMockBuilder(ContextInterface::class)->getMockForAbstractClass();
+        $this->repository = $this->getMockBuilder(Repository::class)->disableOriginalConstructor()->getMock();
+        $this->model = new Placeholder(
+            $this->imageContext,
+            $this->scopeConfig,
+            $this->repository,
+            'thumbnail'
+        );
+    }
+
+    public function testModuleAndContentAndContentType()
+    {
+        $contentType = 'image';
+        $this->assertEquals($contentType, $this->model->getContentType());
+        $this->assertEquals($contentType, $this->model->getSourceContentType());
+        $this->assertNull($this->model->getContent());
+        $this->assertEquals('placeholder', $this->model->getModule());
+    }
+
+    public function testGetFilePath()
+    {
+        $this->assertNull($this->model->getFilePath());
+        $this->scopeConfig->expects($this->once())->method('getValue')->willReturn('default/thumbnail.jpg');
+        $this->assertEquals('default/thumbnail.jpg', $this->model->getFilePath());
+    }
+
+    public function testGetContext()
+    {
+        $this->assertInstanceOf(ContextInterface::class, $this->model->getContext());
+    }
+
+    /**
+     * @param string $imageType
+     * @param string $placeholderPath
+     * @dataProvider getPathDataProvider
+     */
+    public function testGetPathAndGetSourceFile($imageType, $placeholderPath)
+    {
+        $imageModel = new Placeholder(
+            $this->imageContext,
+            $this->scopeConfig,
+            $this->repository,
+            $imageType
+        );
+        $absolutePath = '/var/www/html/magento2ce/pub/media/catalog/product';
+
+        $this->scopeConfig->expects($this->any())
+            ->method('getValue')
+            ->with(
+                "catalog/placeholder/{$imageType}_placeholder",
+                \Magento\Store\Model\ScopeInterface::SCOPE_STORE,
+                null
+            )->willReturn($placeholderPath);
+
+        if ($placeholderPath == null) {
+            $this->imageContext->expects($this->never())->method('getPath');
+            $assetMock = $this->getMockBuilder(\Magento\Framework\View\Asset\MergeableInterface::class)
+                ->getMockForAbstractClass();
+            $expectedResult = 'path/to_default/placeholder/by_type';
+            $assetMock->expects($this->any())->method('getSourceFile')->willReturn($expectedResult);
+            $this->repository->expects($this->any())->method('createAsset')->willReturn($assetMock);
+        } else {
+            $this->imageContext->expects($this->any())->method('getPath')->willReturn($absolutePath);
+            $expectedResult = $absolutePath
+                . DIRECTORY_SEPARATOR . $imageModel->getModule()
+                . DIRECTORY_SEPARATOR . $placeholderPath;
+        }
+
+        $this->assertEquals($expectedResult, $imageModel->getPath());
+        $this->assertEquals($expectedResult, $imageModel->getSourceFile());
+    }
+
+    /**
+     * @param string $imageType
+     * @param string $placeholderPath
+     * @dataProvider getPathDataProvider
+     */
+    public function testGetUrl($imageType, $placeholderPath)
+    {
+        $imageModel = new Placeholder(
+            $this->imageContext,
+            $this->scopeConfig,
+            $this->repository,
+            $imageType
+        );
+
+        $this->scopeConfig->expects($this->any())
+            ->method('getValue')
+            ->with(
+                "catalog/placeholder/{$imageType}_placeholder",
+                \Magento\Store\Model\ScopeInterface::SCOPE_STORE,
+                null
+            )->willReturn($placeholderPath);
+
+        if ($placeholderPath == null) {
+            $this->imageContext->expects($this->never())->method('getBaseUrl');
+            $expectedResult = 'http://localhost/pub/media/catalog/product/to_default/placeholder/by_type';
+            $this->repository->expects($this->any())->method('getUrl')->willReturn($expectedResult);
+        } else {
+            $baseUrl = 'http://localhost/pub/media/catalog/product';
+            $this->imageContext->expects($this->any())->method('getBaseUrl')->willReturn($baseUrl);
+            $expectedResult = $baseUrl
+                . DIRECTORY_SEPARATOR . $imageModel->getModule()
+                . DIRECTORY_SEPARATOR . $placeholderPath;
+        }
+
+        $this->assertEquals($expectedResult, $imageModel->getUrl());
+    }
+
+    public function getPathDataProvider()
+    {
+        return [
+            [
+                'thumbnail',
+                'default/thumbnail.jpg',
+            ],
+            [
+                'non_exist',
+                null,
+            ],
+        ];
+    }
+}
diff --git a/app/code/Magento/Catalog/etc/di.xml b/app/code/Magento/Catalog/etc/di.xml
index 6b39520ae021e9eb3bfafa20bda03e42c7db907a..27c3a0a2f14c80714bf934c167bd8337477cc06a 100644
--- a/app/code/Magento/Catalog/etc/di.xml
+++ b/app/code/Magento/Catalog/etc/di.xml
@@ -48,6 +48,8 @@
     <preference for="Magento\Catalog\Api\Data\CategorySearchResultsInterface" type="Magento\Framework\Api\SearchResults" />
     <preference for="Magento\Catalog\Model\Config\Source\ProductPriceOptionsInterface" type="Magento\Catalog\Model\Config\Source\Product\Options\Price"/>
     <preference for="Magento\Catalog\Model\Indexer\Product\Flat\Table\BuilderInterface" type="Magento\Catalog\Model\Indexer\Product\Flat\Table\Builder"/>
+    <preference for="Magento\Catalog\Model\Product\Media\ConfigInterface" type="Magento\Catalog\Model\Product\Media\Config"/>
+    <preference for="Magento\Framework\View\Asset\ContextInterface" type="Magento\Catalog\Model\View\Asset\Image\Context"/>
     <type name="Magento\Customer\Model\ResourceModel\Visitor">
         <plugin name="catalogLog" type="Magento\Catalog\Model\Plugin\Log" />
     </type>
diff --git a/app/code/Magento/ConfigurableProduct/Helper/Data.php b/app/code/Magento/ConfigurableProduct/Helper/Data.php
index 079aee7b23750bbb08d01ecae4c672c50bf1eeb2..4c1370b1a0d7aed1c786d318c3c65382277e2afe 100644
--- a/app/code/Magento/ConfigurableProduct/Helper/Data.php
+++ b/app/code/Magento/ConfigurableProduct/Helper/Data.php
@@ -50,15 +50,13 @@ class Data
                 );
                 $image->setData(
                     'medium_image_url',
-                    $this->imageHelper->init($product, 'product_page_image_medium')
-                        ->constrainOnly(true)->keepAspectRatio(true)->keepFrame(false)
+                    $this->imageHelper->init($product, 'product_page_image_medium_no_frame')
                         ->setImageFile($image->getFile())
                         ->getUrl()
                 );
                 $image->setData(
                     'large_image_url',
-                    $this->imageHelper->init($product, 'product_page_image_large')
-                        ->constrainOnly(true)->keepAspectRatio(true)->keepFrame(false)
+                    $this->imageHelper->init($product, 'product_page_image_large_no_frame')
                         ->setImageFile($image->getFile())
                         ->getUrl()
                 );
diff --git a/app/code/Magento/ConfigurableProduct/Test/Unit/Helper/DataTest.php b/app/code/Magento/ConfigurableProduct/Test/Unit/Helper/DataTest.php
index be1f4c76a4f45e3051c4d39f87c4619ffac1de3a..efb11e6b13a1d63d4fb9fbb37b5d5a89f5a8f511 100644
--- a/app/code/Magento/ConfigurableProduct/Test/Unit/Helper/DataTest.php
+++ b/app/code/Magento/ConfigurableProduct/Test/Unit/Helper/DataTest.php
@@ -35,7 +35,7 @@ class DataTest extends \PHPUnit_Framework_TestCase
 
     public function testGetAllowAttributes()
     {
-        $typeInstanceMock = $this->getMock(
+        $typeInstanceMock = $this->getMock(
             \Magento\ConfigurableProduct\Model\Product\Type\Configurable::class, [], [], '', false
         );
         $typeInstanceMock->expects($this->once())
@@ -91,7 +91,7 @@ class DataTest extends \PHPUnit_Framework_TestCase
      */
     public function getOptionsDataProvider()
     {
-        $currentProductMock = $this->getMock(
+        $currentProductMock = $this->getMock(
             \Magento\Catalog\Model\Product::class, ['getTypeInstance', '__wakeup'], [], '', false
         );
         $provider = [];
@@ -106,10 +106,10 @@ class DataTest extends \PHPUnit_Framework_TestCase
         $attributesCount = 3;
         $attributes = [];
         for ($i = 1; $i < $attributesCount; $i++) {
-            $attribute = $this->getMock(
+            $attribute = $this->getMock(
                 \Magento\Framework\DataObject::class, ['getProductAttribute'], [], '', false
             );
-            $productAttribute = $this->getMock(
+            $productAttribute = $this->getMock(
                 \Magento\Framework\DataObject::class,
                 ['getId', 'getAttributeCode'],
                 [],
@@ -127,7 +127,7 @@ class DataTest extends \PHPUnit_Framework_TestCase
                 ->will($this->returnValue($productAttribute));
             $attributes[] = $attribute;
         }
-        $typeInstanceMock = $this->getMock(
+        $typeInstanceMock = $this->getMock(
             \Magento\ConfigurableProduct\Model\Product\Type\Configurable::class, [], [], '', false
         );
         $typeInstanceMock->expects($this->any())
@@ -138,7 +138,7 @@ class DataTest extends \PHPUnit_Framework_TestCase
             ->will($this->returnValue($typeInstanceMock));
         $allowedProducts = [];
         for ($i = 1; $i <= 2; $i++) {
-            $productMock = $this->getMock(
+            $productMock = $this->getMock(
                 \Magento\Catalog\Model\Product::class, ['getData', 'getImage', 'getId', '__wakeup', 'getMediaGalleryImages'], [], '', false
             );
             $productMock->expects($this->any())
@@ -195,4 +195,64 @@ class DataTest extends \PHPUnit_Framework_TestCase
         }
         return $map[$key];
     }
+
+    public function testGetGalleryImages()
+    {
+        $productMock = $this->getMockBuilder(\Magento\Catalog\Api\Data\ProductInterface::class)
+            ->setMethods(['getMediaGalleryImages'])
+            ->getMockForAbstractClass();
+        $productMock->expects($this->once())
+            ->method('getMediaGalleryImages')
+            ->willReturn($this->getImagesCollection());
+
+        $this->_imageHelperMock->expects($this->exactly(3))
+            ->method('init')
+            ->willReturnMap([
+                [$productMock, 'product_page_image_small', [], $this->_imageHelperMock],
+                [$productMock, 'product_page_image_medium_no_frame', [], $this->_imageHelperMock],
+                [$productMock, 'product_page_image_large_no_frame', [], $this->_imageHelperMock],
+            ])
+            ->willReturnSelf();
+        $this->_imageHelperMock->expects($this->exactly(3))
+            ->method('setImageFile')
+            ->with('test_file')
+            ->willReturnSelf();
+        $this->_imageHelperMock->expects($this->at(0))
+            ->method('getUrl')
+            ->willReturn('product_page_image_small_url');
+        $this->_imageHelperMock->expects($this->at(1))
+            ->method('getUrl')
+            ->willReturn('product_page_image_medium_url');
+        $this->_imageHelperMock->expects($this->at(2))
+            ->method('getUrl')
+            ->willReturn('product_page_image_large_url');
+
+        $this->assertInstanceOf(
+            \Magento\Framework\Data\Collection::class,
+            $this->_model->getGalleryImages($productMock)
+        );
+
+    }
+
+    /**
+     * @return \Magento\Framework\Data\Collection
+     */
+    private function getImagesCollection()
+    {
+        $collectionMock = $this->getMockBuilder(\Magento\Framework\Data\Collection::class)
+            ->disableOriginalConstructor()
+            ->getMock();
+
+        $items = [
+            new \Magento\Framework\DataObject([
+                'file' => 'test_file'
+            ]),
+        ];
+
+        $collectionMock->expects($this->any())
+            ->method('getIterator')
+            ->willReturn(new \ArrayIterator($items));
+
+        return $collectionMock;
+    }
 }
diff --git a/app/code/Magento/Swatches/Helper/Data.php b/app/code/Magento/Swatches/Helper/Data.php
index a973a822c4101671faa0fe57925a0e9b0c5b2f2a..d80aaaf73ccf016bc1d571254cfc90a7f0452c3d 100644
--- a/app/code/Magento/Swatches/Helper/Data.php
+++ b/app/code/Magento/Swatches/Helper/Data.php
@@ -328,12 +328,10 @@ class Data
     private function getAllSizeImages(ModelProduct $product, $imageFile)
     {
         return [
-            'large' => $this->imageHelper->init($product, 'product_page_image_large')
-                ->constrainOnly(true)->keepAspectRatio(true)->keepFrame(false)
+            'large' => $this->imageHelper->init($product, 'product_page_image_large_no_frame')
                 ->setImageFile($imageFile)
                 ->getUrl(),
-            'medium' => $this->imageHelper->init($product, 'product_page_image_medium')
-                ->constrainOnly(true)->keepAspectRatio(true)->keepFrame(false)
+            'medium' => $this->imageHelper->init($product, 'product_page_image_medium_no_frame')
                 ->setImageFile($imageFile)
                 ->getUrl(),
             'small' => $this->imageHelper->init($product, 'product_page_image_small')
diff --git a/app/code/Magento/Swatches/Test/Unit/Helper/DataTest.php b/app/code/Magento/Swatches/Test/Unit/Helper/DataTest.php
index e5f2f887836eff5ad73ca95602d4d7aec411150e..10f26acfc2594499adb80cd6df5bfee1fc277f88 100644
--- a/app/code/Magento/Swatches/Test/Unit/Helper/DataTest.php
+++ b/app/code/Magento/Swatches/Test/Unit/Helper/DataTest.php
@@ -340,8 +340,8 @@ class DataTest extends \PHPUnit_Framework_TestCase
         $this->imageHelperMock->expects($this->any())
             ->method('init')
             ->willReturnMap([
-                [$this->productMock, 'product_page_image_large', [], $this->imageHelperMock],
-                [$this->productMock, 'product_page_image_medium', [], $this->imageHelperMock],
+                [$this->productMock, 'product_page_image_large_no_frame', [], $this->imageHelperMock],
+                [$this->productMock, 'product_page_image_medium_no_frame', [], $this->imageHelperMock],
                 [$this->productMock, 'product_page_image_small', [], $this->imageHelperMock],
             ]);
 
@@ -349,15 +349,6 @@ class DataTest extends \PHPUnit_Framework_TestCase
             ->method('setImageFile')
             ->with($image)
             ->willReturnSelf();
-        $this->imageHelperMock->expects($this->any())
-            ->method('constrainOnly')
-            ->willReturnSelf();
-        $this->imageHelperMock->expects($this->any())
-            ->method('keepAspectRatio')
-            ->willReturnSelf();
-        $this->imageHelperMock->expects($this->any())
-            ->method('keepFrame')
-            ->willReturnSelf();
         $this->imageHelperMock->expects($this->any())
             ->method('getUrl')
             ->willReturn('http://full_path_to_image/magento1.png');
diff --git a/app/design/frontend/Magento/blank/etc/view.xml b/app/design/frontend/Magento/blank/etc/view.xml
index 6c53a8613f09ca1f91c9e024b8e2cec723f0b309..3bf5fc6fc77af5a67becb60920343c6f6ce48db8 100644
--- a/app/design/frontend/Magento/blank/etc/view.xml
+++ b/app/design/frontend/Magento/blank/etc/view.xml
@@ -73,10 +73,18 @@
                 <height>140</height>
             </image>
             <image id="product_page_image_large" type="image"/>
+            <image id="product_page_image_large_no_frame" type="image">
+                <frame>false</frame>
+            </image>
             <image id="product_page_image_medium" type="image">
                 <width>700</width>
                 <height>700</height>
             </image>
+            <image id="product_page_image_medium_no_frame" type="image">
+                <width>700</width>
+                <height>700</height>
+                <frame>false</frame>
+            </image>
             <image id="product_page_image_small" type="thumbnail">
                 <width>90</width>
                 <height>90</height>
diff --git a/app/design/frontend/Magento/luma/etc/view.xml b/app/design/frontend/Magento/luma/etc/view.xml
index 12a51ee065edba43c9afde39ce31c2aace3d00a8..33fc64af977c557f17fd7e9db0779a881fa77e08 100644
--- a/app/design/frontend/Magento/luma/etc/view.xml
+++ b/app/design/frontend/Magento/luma/etc/view.xml
@@ -77,10 +77,18 @@
                 <height>140</height>
             </image>
             <image id="product_page_image_large" type="image"/>
+            <image id="product_page_image_large_no_frame" type="image">
+                <frame>false</frame>
+            </image>
             <image id="product_page_image_medium" type="image">
                 <width>700</width>
                 <height>560</height>
             </image>
+            <image id="product_page_image_medium_no_frame" type="image">
+                <width>700</width>
+                <height>700</height>
+                <frame>false</frame>
+            </image>
             <image id="product_page_image_small" type="thumbnail">
                 <width>88</width>
                 <height>110</height>
diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/ImageTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/ImageTest.php
index 88fd7797e768fb5fb227a8fc888ad481794d4347..8b0d1b393f467641bd87bc65fb425dbe8000bff6 100644
--- a/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/ImageTest.php
+++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/ImageTest.php
@@ -23,8 +23,15 @@ class ImageTest extends \PHPUnit_Framework_TestCase
         $model = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create(
             \Magento\Catalog\Model\Product\Image::class
         );
-        $model->setDestinationSubdir('image')->setBaseFile('');
-        $this->assertEmpty($model->getBaseFile());
+        /** @var \Magento\Catalog\Model\View\Asset\Placeholder $defualtPlaceholder */
+        $defualtPlaceholder = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()
+            ->create(\Magento\Catalog\Model\View\Asset\Placeholder::class,
+                ['type' => 'image']
+            );
+
+        $model->setDestinationSubdir('image');
+        $model->setBaseFile('');
+        $this->assertEquals($defualtPlaceholder->getSourceFile(), $model->getBaseFile());
         return $model;
     }