[prev in list] [next in list] [prev in thread] [next in thread] 

List:       kde-commits
Subject:    [calligra/calligra/2.9] krita/plugins/formats/psd: Make reading of PSD pixel data shared between PSD
From:       Dmitry Kazakov <dimula73 () gmail ! com>
Date:       2015-08-13 17:15:19
Message-ID: E1ZPw63-0001eb-JW () scm ! kde ! org
[Download RAW message or body]

Git commit b22044430069ccfa757ff2f461acfd614e8093d8 by Dmitry Kazakov.
Committed on 13/08/2015 at 15:46.
Pushed by dkazakov into branch 'calligra/2.9'.

Make reading of PSD pixel data shared between PSDLayerRecord and PSDImageData

M  +28   -477  krita/plugins/formats/psd/psd_image_data.cpp
M  +14   -99   krita/plugins/formats/psd/psd_layer_record.cpp
M  +0    -3    krita/plugins/formats/psd/psd_layer_record.h
M  +329  -0    krita/plugins/formats/psd/psd_pixel_utils.cpp
M  +12   -235  krita/plugins/formats/psd/psd_pixel_utils.h

http://commits.kde.org/calligra/b22044430069ccfa757ff2f461acfd614e8093d8

diff --git a/krita/plugins/formats/psd/psd_image_data.cpp \
b/krita/plugins/formats/psd/psd_image_data.cpp index b904535..f609110 100644
--- a/krita/plugins/formats/psd/psd_image_data.cpp
+++ b/krita/plugins/formats/psd/psd_image_data.cpp
@@ -36,6 +36,11 @@
 #include "kis_iterator_ng.h"
 #include "kis_paint_device.h"
 
+#include <asl/kis_asl_reader_utils.h>
+
+#include "psd_pixel_utils.h"
+
+
 PSDImageData::PSDImageData(PSDHeader *header)
 {
     m_header = header;
@@ -72,38 +77,10 @@ bool PSDImageData::read(QIODevice *io, KisPaintDeviceSP dev ) {
 
         }
 
-        switch (m_header->colormode) {
-        case Bitmap:
-            break;
-        case Grayscale:
-            readGrayscale(io,dev);
-            break;
-        case Indexed:
-            break;
-        case RGB:
-            readRGB(io, dev);
-            break;
-        case CMYK:
-            readCMYK(io, dev);
-            break;
-        case MultiChannel:
-            break;
-        case DuoTone:
-            break;
-        case Lab:
-            readLAB(io, dev);
-            break;
-        case UNKNOWN:
-            break;
-        default:
-            break;
-        }
-
         break;
 
     case 1: // RLE
     {
-
         quint32 rlelength = 0;
 
         // The start of the actual channel data is _after_ the RLE rowlengths block
@@ -136,88 +113,37 @@ bool PSDImageData::read(QIODevice *io, KisPaintDeviceSP dev ) {
             m_channelInfoRecords.append(channelInfo);
         }
 
-        switch (m_header->colormode) {
-        case Bitmap:
-            break;
-        case Grayscale:
-            readGrayscale(io,dev);
-            break;
-        case Indexed:
-            break;
-        case RGB:
-            readRGB(io, dev);
-            break;
-        case CMYK:
-            readCMYK(io, dev);
-            break;
-        case MultiChannel:
-            break;
-        case DuoTone:
-            break;
-        case Lab:
-            readLAB(io, dev);
-            break;
-        case UNKNOWN:
-            break;
-        default:
-            break;
-        }
-
         break;
     }
     case 2: // ZIP without prediction
+    case 3: // ZIP with prediction
+    default:
+        break;
+    }
 
-        switch (m_header->colormode) {
-        case Bitmap:
-            break;
-        case Grayscale:
-            break;
-        case Indexed:
-            break;
-        case RGB:
-            break;
-        case CMYK:
-            break;
-        case MultiChannel:
-            break;
-        case DuoTone:
-            break;
-        case Lab:
-            break;
-        case UNKNOWN:
-            break;
-        default:
-            break;
+    if (!m_channelInfoRecords.isEmpty()) {
+        QVector<ChannelInfo*> infoRecords;
+
+        QVector<ChannelInfo>::iterator it = m_channelInfoRecords.begin();
+        QVector<ChannelInfo>::iterator end = m_channelInfoRecords.end();
+
+        for (; it != end; ++it) {
+            infoRecords << &(*it);
         }
-        break;
 
-    case 3: // ZIP with prediction
-        switch (m_header->colormode) {
-        case Bitmap:
-            break;
-        case Grayscale:
-            break;
-        case Indexed:
-            break;
-        case RGB:
-            break;
-        case CMYK:
-            break;
-        case MultiChannel:
-            break;
-        case DuoTone:
-            break;
-        case Lab:
-            break;
-        case UNKNOWN:
-            break;
-        default:
-            break;
+        const QRect imageRect(0, 0, m_header->width, m_header->height);
+
+        try {
+            PsdPixelUtils::readChannels(io, dev,
+                                        m_header->colormode,
+                                        m_channelSize,
+                                        imageRect,
+                                        infoRecords);
+        } catch (KisAslReaderUtils::ASLParseException &e) {
+            dev->clear();
+            return true;
         }
 
-        break;
-    default:
-        break;
     }
 
     return true;
@@ -321,378 +247,3 @@ bool PSDImageData::write(QIODevice *io, KisPaintDeviceSP dev)
 
     return true;
 }
-
-bool PSDImageData::readRGB(QIODevice *io, KisPaintDeviceSP dev) {
-
-    int channelid = 0;
-
-    for (quint32 row = 0; row < m_header->height; row++) {
-
-        KisHLineIteratorSP it = dev->createHLineIteratorNG(0, row, m_header->width);
-        QVector<QByteArray> channelBytes;
-
-        for (int channel = 0; channel < m_header->nChannels; channel++) {
-
-            switch (m_compression) {
-            case Compression::Uncompressed:
-            {
-                io->seek(m_channelInfoRecords[channel].channelDataStart + \
                m_channelOffsets[0]);
-                channelBytes.append(io->read(m_header->width*m_channelSize));
-            }
-                break;
-            case Compression::RLE:
-            {
-                io->seek(m_channelInfoRecords[channel].channelDataStart + \
                m_channelOffsets[channel]);
-                int uncompressedLength = m_header->width * m_header->channelDepth / 8;
-                QByteArray compressedBytes = \
                io->read(m_channelInfoRecords[channel].rleRowLengths[row]);
-                QByteArray uncompressedBytes = Compression::uncompress(uncompressedLength, \
                compressedBytes, m_channelInfoRecords[channel].compressionType);
-                channelBytes.append(uncompressedBytes);
-                m_channelOffsets[channel] +=  \
                m_channelInfoRecords[channel].rleRowLengths[row];
-
-            }
-                break;
-            case Compression::ZIP:
-                break;
-            case Compression::ZIPWithPrediction:
-                break;
-
-            default:
-                break;
-            }
-
-        }
-
-        if (m_channelInfoRecords[channelid].compressionType == 0){
-            m_channelOffsets[channelid] += (m_header->width * m_channelSize);
-        }
-
-        for (quint32 col = 0; col < m_header->width; col++) {
-
-            if (m_channelSize == 1) {
-
-                quint8 red = channelBytes[0].constData()[col];
-                KoBgrU8Traits::setRed(it->rawData(), red);
-
-                quint8 green = channelBytes[1].constData()[col];
-                KoBgrU8Traits::setGreen(it->rawData(), green);
-
-                quint8 blue = channelBytes[2].constData()[col];
-                KoBgrU8Traits::setBlue(it->rawData(), blue);
-
-            }
-            else if (m_channelSize == 2) {
-
-                quint16 red = ntohs(reinterpret_cast<const quint16 \
                *>(channelBytes[0].constData())[col]);
-                KoBgrU16Traits::setRed(it->rawData(), red);
-
-                quint16 green = ntohs(reinterpret_cast<const quint16 \
                *>(channelBytes[1].constData())[col]);
-                KoBgrU16Traits::setGreen(it->rawData(), green);
-
-                quint16 blue = ntohs(reinterpret_cast<const quint16 \
                *>(channelBytes[2].constData())[col]);
-                KoBgrU16Traits::setBlue(it->rawData(), blue);
-
-            }
-            else if (m_channelSize == 4) {
-
-                quint32 red = ntohl(reinterpret_cast<const quint32 \
                *>(channelBytes.constData())[col]);
-                KoBgrTraits<quint32>::setRed(it->rawData(), red);
-
-                quint32 green = ntohl(reinterpret_cast<const quint32 \
                *>(channelBytes.constData())[col]);
-                KoBgrTraits<quint32>::setGreen(it->rawData(), green);
-
-                quint32 blue = ntohl(reinterpret_cast<const quint32 \
                *>(channelBytes.constData())[col]);
-                KoBgrTraits<quint32>::setBlue(it->rawData(), blue);
-
-            }
-
-            dev->colorSpace()->setOpacity(it->rawData(), OPACITY_OPAQUE_U8, 1);
-            it->nextPixel();
-        }
-
-    }
-
-    return true;
-}
-
-
-bool PSDImageData::readCMYK(QIODevice *io, KisPaintDeviceSP dev) {
-
-    int channelid = 0;
-
-    for (quint32 row = 0; row < m_header->height; row++) {
-
-        KisHLineIteratorSP it = dev->createHLineIteratorNG(0, row, m_header->width);
-        QVector<QByteArray> channelBytes;
-
-        for (int channel = 0; channel < m_header->nChannels; channel++) {
-
-
-            switch (m_compression) {
-
-            case Compression::Uncompressed:
-
-            {
-                io->seek(m_channelInfoRecords[channel].channelDataStart + \
                m_channelOffsets[0]);
-                channelBytes.append(io->read(m_header->width*m_channelSize));
-
-            }
-                break;
-
-            case Compression::RLE:
-            {
-                io->seek(m_channelInfoRecords[channel].channelDataStart + \
                m_channelOffsets[channel]);
-                int uncompressedLength = m_header->width * m_header->channelDepth / 8;
-                QByteArray compressedBytes = \
                io->read(m_channelInfoRecords[channel].rleRowLengths[row]);
-                QByteArray uncompressedBytes = Compression::uncompress(uncompressedLength, \
                compressedBytes, m_channelInfoRecords[channel].compressionType);
-                channelBytes.append(uncompressedBytes);
-                m_channelOffsets[channel] +=  \
                m_channelInfoRecords[channel].rleRowLengths[row];
-            }
-                break;
-
-            case Compression::ZIP:
-                break;
-
-            case Compression::ZIPWithPrediction:
-                break;
-
-            default:
-                break;
-            }
-
-        }
-
-        if (m_channelInfoRecords[channelid].compressionType == 0){
-            m_channelOffsets[channelid] += (m_header->width * m_channelSize);
-        }
-
-        for (quint32 col = 0; col < m_header->width; col++) {
-
-            if (m_channelSize == 1) {
-
-                quint8 *pixel = new quint8[5];
-                memset(pixel, 0, 5);
-                dev->colorSpace()->setOpacity(pixel, OPACITY_OPAQUE_U8, 1);
-
-                memset(pixel, 255 - channelBytes[0].constData()[col], 1);
-                memset(pixel + 1, 255 - channelBytes[1].constData()[col], 1);
-                memset(pixel + 2, 255 - channelBytes[2].constData()[col], 1);
-                memset(pixel + 3, 255 - channelBytes[3].constData()[col], 1);
-                dbgFile << "C" << pixel[0] << "M" << pixel[1] << "Y" << pixel[2] << "K" << \
                pixel[3] << "A" << pixel[4];
-                memcpy(it->rawData(), pixel, 5);
-
-
-            }
-            else if (m_channelSize == 2) {
-
-                quint16 C = ntohs(reinterpret_cast<const quint16 \
                *>(channelBytes[0].constData())[col]);
-                KoCmykTraits<quint16>::setC(it->rawData(),C);
-
-                quint16 M = ntohs(reinterpret_cast<const quint16 \
                *>(channelBytes[1].constData())[col]);
-                KoCmykTraits<quint16>::setM(it->rawData(),M);
-
-                quint16 Y = ntohs(reinterpret_cast<const quint16 \
                *>(channelBytes[2].constData())[col]);
-                KoCmykTraits<quint16>::setY(it->rawData(),Y);
-
-                quint16 K = ntohs(reinterpret_cast<const quint16 \
                *>(channelBytes[3].constData())[col]);
-                KoCmykTraits<quint16>::setK(it->rawData(),K);
-
-            }
-            else if (m_channelSize == 4) {
-
-                quint32 C = ntohl(reinterpret_cast<const quint32 \
                *>(channelBytes[0].constData())[col]);
-                KoCmykTraits<quint32>::setC(it->rawData(),C);
-
-                quint32 M = ntohl(reinterpret_cast<const quint32 \
                *>(channelBytes[1].constData())[col]);
-                KoCmykTraits<quint32>::setM(it->rawData(),M);
-
-                quint32 Y = ntohl(reinterpret_cast<const quint32 \
                *>(channelBytes[2].constData())[col]);
-                KoCmykTraits<quint32>::setY(it->rawData(),Y);
-
-                quint32 K = ntohl(reinterpret_cast<const quint32 \
                *>(channelBytes[3].constData())[col]);
-                KoCmykTraits<quint32>::setK(it->rawData(),K);
-
-            }
-
-            dev->colorSpace()->setOpacity(it->rawData(), OPACITY_OPAQUE_U8, 1);
-            it->nextPixel();
-        }
-
-    }
-
-    return true;
-
-}
-
-bool PSDImageData::readLAB(QIODevice *io, KisPaintDeviceSP dev) {
-
-    int channelid = 0;
-
-    for (quint32 row = 0; row < m_header->height; row++) {
-
-        KisHLineIteratorSP it = dev->createHLineIteratorNG(0, row, m_header->width);
-        QVector<QByteArray> channelBytes;
-
-        for (int channel = 0; channel < m_header->nChannels; channel++) {
-
-
-            switch (m_compression) {
-
-            case Compression::Uncompressed:
-
-            {
-                io->seek(m_channelInfoRecords[channel].channelDataStart + \
                m_channelOffsets[0]);
-                channelBytes.append(io->read(m_header->width*m_channelSize));
-            }
-                break;
-
-            case Compression::RLE:
-            {
-                io->seek(m_channelInfoRecords[channel].channelDataStart + \
                m_channelOffsets[channel]);
-                int uncompressedLength = m_header->width * m_header->channelDepth / 8;
-                QByteArray compressedBytes = \
                io->read(m_channelInfoRecords[channel].rleRowLengths[row]);
-                QByteArray uncompressedBytes = Compression::uncompress(uncompressedLength, \
                compressedBytes, m_channelInfoRecords[channel].compressionType);
-                channelBytes.append(uncompressedBytes);
-                m_channelOffsets[channel] +=  \
                m_channelInfoRecords[channel].rleRowLengths[row];
-
-            }
-                break;
-
-            case Compression::ZIP:
-                break;
-
-            case Compression::ZIPWithPrediction:
-                break;
-
-            default:
-                break;
-            }
-
-        }
-
-        if (m_channelInfoRecords[channelid].compressionType == 0){
-            m_channelOffsets[channelid] += (m_header->width * m_channelSize);
-        }
-
-        for (quint32 col = 0; col < m_header->width; col++) {
-
-            if (m_channelSize == 1) {
-
-                quint8 L = channelBytes[0].constData()[col];
-                KoLabTraits<quint8>::setL(it->rawData(), L);
-
-                quint8 A = channelBytes[1].constData()[col];
-                KoLabTraits<quint8>::setA(it->rawData(), A);
-
-                quint8 B = channelBytes[2].constData()[col];
-                KoLabTraits<quint8>::setB(it->rawData(), B);
-            }
-
-            else if (m_channelSize == 2) {
-
-                quint16 L = ntohs(reinterpret_cast<const quint16 \
                *>(channelBytes[0].constData())[col]);
-                KoLabU16Traits::setL(it->rawData(),L);
-
-                quint16 A = ntohs(reinterpret_cast<const quint16 \
                *>(channelBytes[1].constData())[col]);
-                KoLabU16Traits::setA(it->rawData(),A);
-
-                quint16 B = ntohs(reinterpret_cast<const quint16 \
                *>(channelBytes[2].constData())[col]);
-                KoLabU16Traits::setB(it->rawData(),B);
-
-            }
-
-            else if (m_channelSize == 4) {
-
-                quint32 L = ntohl(reinterpret_cast<const quint32 \
                *>(channelBytes[0].constData())[col]);
-                KoLabTraits<quint32>::setL(it->rawData(),L);
-
-                quint32 A = ntohl(reinterpret_cast<const quint32 \
                *>(channelBytes[1].constData())[col]);
-                KoLabTraits<quint32>::setA(it->rawData(),A);
-
-                quint32 B = ntohl(reinterpret_cast<const quint32 \
                *>(channelBytes[2].constData())[col]);
-                KoLabTraits<quint32>::setB(it->rawData(),B);
-
-            }
-
-            dev->colorSpace()->setOpacity(it->rawData(), OPACITY_OPAQUE_U8, 1);
-            it->nextPixel();
-        }
-
-    }
-
-    return true;
-}
-
-bool PSDImageData::readGrayscale(QIODevice *io, KisPaintDeviceSP dev) {
-    int channelid = 0;
-
-    for (quint32 row = 0; row < m_header->height; row++) {
-
-        KisHLineIteratorSP it = dev->createHLineIteratorNG(0, row, m_header->width);
-        QVector<QByteArray> channelBytes;
-
-        for (int channel = 0; channel < m_header->nChannels; channel++) {
-
-            switch (m_compression) {
-            case Compression::Uncompressed:
-            {
-                io->seek(m_channelInfoRecords[channel].channelDataStart + \
                m_channelOffsets[0]);
-                channelBytes.append(io->read(m_header->width*m_channelSize));
-            }
-                break;
-            case Compression::RLE:
-            {
-                io->seek(m_channelInfoRecords[channel].channelDataStart + \
                m_channelOffsets[channel]);
-                int uncompressedLength = m_header->width * m_header->channelDepth / 8;
-                QByteArray compressedBytes = \
                io->read(m_channelInfoRecords[channel].rleRowLengths[row]);
-                QByteArray uncompressedBytes = Compression::uncompress(uncompressedLength, \
                compressedBytes, m_channelInfoRecords[channel].compressionType);
-                channelBytes.append(uncompressedBytes);
-                m_channelOffsets[channel] +=  \
                m_channelInfoRecords[channel].rleRowLengths[row];
-
-            }
-                break;
-            case Compression::ZIP:
-                break;
-            case Compression::ZIPWithPrediction:
-                break;
-
-            default:
-                break;
-            }
-
-        }
-
-        if (m_channelInfoRecords[channelid].compressionType == 0) {
-            m_channelOffsets[channelid] += (m_header->width * m_channelSize);
-        }
-
-        for (quint32 col = 0; col < m_header->width; col++) {
-
-            if (m_channelSize == 1) {
-
-                quint8 Gray = channelBytes[0].constData()[col];
-                KoGrayU8Traits::setGray(it->rawData(), Gray);
-
-            }
-
-            else if (m_channelSize == 2) {
-
-                quint16 Gray = ntohs(reinterpret_cast<const quint16 \
                *>(channelBytes[0].constData())[col]);
-                KoGrayU16Traits::setGray(it->rawData(), Gray);
-
-            }
-
-            else if (m_channelSize == 4) {
-
-                quint32 Gray = ntohl(reinterpret_cast<const quint32 \
                *>(channelBytes[0].constData())[col]);
-                KoGrayTraits<quint32>::setGray(it->rawData(), Gray);
-
-            }
-
-            dev->colorSpace()->setOpacity(it->rawData(), OPACITY_OPAQUE_U8, 1);
-            it->nextPixel();
-        }
-
-    }
-
-    return true;
-}
diff --git a/krita/plugins/formats/psd/psd_layer_record.cpp \
b/krita/plugins/formats/psd/psd_layer_record.cpp index 16fc808..b7b391c 100644
--- a/krita/plugins/formats/psd/psd_layer_record.cpp
+++ b/krita/plugins/formats/psd/psd_layer_record.cpp
@@ -39,11 +39,11 @@
 
 #include <asl/kis_offset_keeper.h>
 #include <asl/kis_asl_writer_utils.h>
+#include <asl/kis_asl_reader_utils.h>
 
 #include "psd_pixel_utils.h"
 
 
-
 // Just for pretty debug messages
 QString channelIdToChannelType(int channelId, psd_color_mode colormode)
 {
@@ -747,33 +747,22 @@ bool PSDLayerRecord::valid()
 bool PSDLayerRecord::readPixelData(QIODevice *io, KisPaintDeviceSP device)
 {
     dbgFile << "Reading pixel data for layer" << layerName << "pos" << io->pos();
-    switch (m_header.colormode) {
-    case Bitmap:
-        error = "Unsupported color mode: bitmap";
-        return false; // Not supported;
-    case Indexed:
-        error = "Unsupported color mode: indexed";
-        return false; // Not supported;
-    case MultiChannel:
-        error = "Unsupported color mode: indexed";
-        return false; // Not supported
-    case DuoTone:
-        error = "Unsupported color mode: Duotone";
-        return false; // Not supported
-    case Grayscale:
-        return readCommon(device, io, &PsdPixelUtils::readGrayPixelCommon);
-    case RGB:
-        return readCommon(device, io, &PsdPixelUtils::readRgbPixelCommon);
-    case CMYK:
-        return readCommon(device, io, &PsdPixelUtils::readCmykPixelCommon);
-    case Lab:
-        return readCommon(device, io, &PsdPixelUtils::readLabPixelCommon);
-    case UNKNOWN:
-    default:
+
+    const int channelSize = m_header.channelDepth / 8;
+    const QRect layerRect = QRect(left,
+                                  top,
+                                  right - left,
+                                  bottom - top);
+
+    try {
+        PsdPixelUtils::readChannels(io, device, m_header.colormode, channelSize, layerRect, \
channelInfoRecords); +    } catch (KisAslReaderUtils::ASLParseException &e) {
+        device->clear();
+        error = e.what();
         return false;
     }
 
-    return false;
+    return true;
 }
 
 QRect PSDLayerRecord::channelRect(ChannelInfo *channel) const
@@ -858,80 +847,6 @@ bool PSDLayerRecord::readMask(QIODevice *io, KisPaintDeviceSP dev, \
ChannelInfo *  return true;
 }
 
-#include <asl/kis_asl_reader_utils.h>
-
-QMap<quint16, QByteArray> fetchChannelsBytes(QIODevice *io, QVector<ChannelInfo*> \
                channelInfoRecords,
-                                            int row, int width, int channelSize)
-{
-    const int uncompressedLength = width * channelSize;
-
-    QMap<quint16, QByteArray> channelBytes;
-
-    foreach(ChannelInfo *channelInfo, channelInfoRecords) {
-        // user supplied masks are ignored here
-        if (channelInfo->channelId < -1) continue;
-
-        io->seek(channelInfo->channelDataStart + channelInfo->channelOffset);
-
-        if (channelInfo->compressionType == Compression::Uncompressed) {
-            channelBytes[channelInfo->channelId] = io->read(uncompressedLength);
-            channelInfo->channelOffset += uncompressedLength;
-        }
-        else if (channelInfo->compressionType == Compression::RLE) {
-            int rleLength = channelInfo->rleRowLengths[row];
-            QByteArray compressedBytes = io->read(rleLength);
-            QByteArray uncompressedBytes = Compression::uncompress(uncompressedLength, \
                compressedBytes, channelInfo->compressionType);
-            channelBytes.insert(channelInfo->channelId, uncompressedBytes);
-            channelInfo->channelOffset += rleLength;
-        }
-        else {
-            QString error = QString("Unsupported Compression mode: \
                %1").arg(channelInfo->compressionType);
-            dbgFile << "ERROR: fetchChannelsBytes:" << error;
-            throw KisAslReaderUtils::ASLParseException(error);
-        }
-    }
-
-    return channelBytes;
-}
-
-bool PSDLayerRecord::readCommon(KisPaintDeviceSP dev, QIODevice *io, PixelFunc pixelFunc)
-{
-    KisOffsetKeeper keeper(io);
-
-    qint64 width = right - left;
-
-    if (width <= 0) {
-        dbgFile << "Empty layer";
-        return true;
-    }
-
-    const int channelSize = m_header.channelDepth / 8;
-
-    KisHLineIteratorSP it = dev->createHLineIteratorNG(left, top, width);
-    for (int row = top ; row < bottom; row++)
-    {
-
-        QMap<quint16, QByteArray> channelBytes;
-
-        try {
-            channelBytes = fetchChannelsBytes(io, channelInfoRecords,
-                                             row - top, width, channelSize);
-        } catch (KisAslReaderUtils::ASLParseException &e) {
-            dev->clear();
-            error = e.what();
-            return false;
-        }
-
-        for (qint64 col = 0; col < width; col++){
-            pixelFunc(channelSize, channelBytes, col, it->rawData());
-            it->nextPixel();
-        }
-        it->nextRow();
-    }
-
-    return true;
-}
-
 QDebug operator<<(QDebug dbg, const PSDLayerRecord &layer)
 {
 #ifndef NODEBUG
diff --git a/krita/plugins/formats/psd/psd_layer_record.h \
b/krita/plugins/formats/psd/psd_layer_record.h index 655545b..a58a3e83 100644
--- a/krita/plugins/formats/psd/psd_layer_record.h
+++ b/krita/plugins/formats/psd/psd_layer_record.h
@@ -160,9 +160,6 @@ private:
 
 private:
 
-    typedef boost::function<void(int, const QMap<quint16, QByteArray>&, int, quint8*)> \
                PixelFunc;
-    bool readCommon(KisPaintDeviceSP dev, QIODevice *io, PixelFunc pixelFunc);
-
     bool readRGB(KisPaintDeviceSP dev ,QIODevice *io);
     bool readCMYK(KisPaintDeviceSP dev ,QIODevice *io);
     bool readLAB(KisPaintDeviceSP dev ,QIODevice *io);
diff --git a/krita/plugins/formats/psd/psd_pixel_utils.cpp \
b/krita/plugins/formats/psd/psd_pixel_utils.cpp index bfe6303..50be4b9 100644
--- a/krita/plugins/formats/psd/psd_pixel_utils.cpp
+++ b/krita/plugins/formats/psd/psd_pixel_utils.cpp
@@ -18,6 +18,335 @@
 
 #include "psd_pixel_utils.h"
 
+#include <QtGlobal>
+#include <QMap>
+#include <QIODevice>
+
+
+#include <KoColorSpace.h>
+#include <KoColorSpaceMaths.h>
+#include <KoColorSpaceTraits.h>
+#include <KoCmykColorSpaceTraits.h>
+
+#include <netinet/in.h> // htonl
+
+#include <asl/kis_asl_reader_utils.h>
+
+#include "psd_layer_record.h"
+#include <asl/kis_offset_keeper.h>
+#include "kis_iterator_ng.h"
+
+
 namespace PsdPixelUtils {
 
+template <class Traits>
+typename Traits::channels_type convertByteOrder(typename Traits::channels_type value);
+// default implementation is undefined for every color space should be added manually
+
+template <>
+inline quint8 convertByteOrder<KoGrayU8Traits>(quint8 value) {
+    return value;
+}
+
+template <>
+inline quint16 convertByteOrder<KoGrayU16Traits>(quint16 value) {
+    return ntohs(value);
+}
+
+template <>
+inline quint32 convertByteOrder<KoGrayU32Traits>(quint32 value) {
+    return ntohs(value);
+}
+
+template <>
+inline quint8 convertByteOrder<KoBgrU8Traits>(quint8 value) {
+    return value;
+}
+
+template <>
+inline quint16 convertByteOrder<KoBgrU16Traits>(quint16 value) {
+    return ntohs(value);
+}
+
+template <>
+inline quint32 convertByteOrder<KoBgrU32Traits>(quint32 value) {
+    return ntohs(value);
+}
+
+template <>
+inline quint8 convertByteOrder<KoCmykU8Traits>(quint8 value) {
+    return value;
+}
+
+template <>
+inline quint16 convertByteOrder<KoCmykU16Traits>(quint16 value) {
+    return ntohs(value);
+}
+
+template <>
+inline float convertByteOrder<KoCmykF32Traits>(float value) {
+    return ntohs(value);
+}
+
+template <>
+inline quint8 convertByteOrder<KoLabU8Traits>(quint8 value) {
+    return value;
+}
+
+template <>
+inline quint16 convertByteOrder<KoLabU16Traits>(quint16 value) {
+    return ntohs(value);
+}
+
+template <>
+inline float convertByteOrder<KoLabF32Traits>(float value) {
+    return ntohs(value);
+}
+
+template <class Traits>
+void readGrayPixel(const QMap<quint16, QByteArray> &channelBytes,
+                  int col, quint8 *dstPtr)
+{
+    typedef typename Traits::Pixel Pixel;
+    typedef typename Traits::channels_type channels_type;
+
+    const channels_type unitValue = KoColorSpaceMathsTraits<channels_type>::unitValue;
+    channels_type opacity = unitValue;
+    if (channelBytes.contains(-1)) {
+        opacity = channelBytes[-1].constData()[col];
+    }
+
+    Pixel *pixelPtr = reinterpret_cast<Pixel*>(dstPtr);
+
+    channels_type gray = convertByteOrder<Traits>(reinterpret_cast<const channels_type \
*>(channelBytes[0].constData())[col]); +
+    pixelPtr->gray = gray;
+    pixelPtr->alpha = opacity;
+}
+
+template <class Traits>
+void readRgbPixel(const QMap<quint16, QByteArray> &channelBytes,
+                  int col, quint8 *dstPtr)
+{
+    typedef typename Traits::Pixel Pixel;
+    typedef typename Traits::channels_type channels_type;
+
+    const channels_type unitValue = KoColorSpaceMathsTraits<channels_type>::unitValue;
+    channels_type opacity = unitValue;
+    if (channelBytes.contains(-1)) {
+        opacity = channelBytes[-1].constData()[col];
+    }
+
+    Pixel *pixelPtr = reinterpret_cast<Pixel*>(dstPtr);
+
+    channels_type blue = convertByteOrder<Traits>(reinterpret_cast<const channels_type \
*>(channelBytes[2].constData())[col]); +    channels_type green = \
convertByteOrder<Traits>(reinterpret_cast<const channels_type \
*>(channelBytes[1].constData())[col]); +    channels_type red = \
convertByteOrder<Traits>(reinterpret_cast<const channels_type \
*>(channelBytes[0].constData())[col]); +
+    pixelPtr->blue = blue;
+    pixelPtr->green = green;
+    pixelPtr->red = red;
+    pixelPtr->alpha = opacity;
+}
+
+template <class Traits>
+void readCmykPixel(const QMap<quint16, QByteArray> &channelBytes,
+                       int col, quint8 *dstPtr)
+{
+    typedef typename Traits::Pixel Pixel;
+    typedef typename Traits::channels_type channels_type;
+
+    const channels_type unitValue = KoColorSpaceMathsTraits<channels_type>::unitValue;
+    channels_type opacity = unitValue;
+    if (channelBytes.contains(-1)) {
+        opacity = channelBytes[-1].constData()[col];
+    }
+
+    Pixel *pixelPtr = reinterpret_cast<Pixel*>(dstPtr);
+
+    channels_type cyan = unitValue - convertByteOrder<Traits>(reinterpret_cast<const \
channels_type *>(channelBytes[0].constData())[col]); +    channels_type magenta = unitValue - \
convertByteOrder<Traits>(reinterpret_cast<const channels_type \
*>(channelBytes[1].constData())[col]); +    channels_type yellow = unitValue - \
convertByteOrder<Traits>(reinterpret_cast<const channels_type \
*>(channelBytes[2].constData())[col]); +    channels_type black = unitValue - \
convertByteOrder<Traits>(reinterpret_cast<const channels_type \
*>(channelBytes[3].constData())[col]); +
+    pixelPtr->cyan = cyan;
+    pixelPtr->magenta = magenta;
+    pixelPtr->yellow = yellow;
+    pixelPtr->black = black;
+    pixelPtr->alpha = opacity;
+}
+
+template <class Traits>
+void readLabPixel(const QMap<quint16, QByteArray> &channelBytes,
+                  int col, quint8 *dstPtr)
+{
+    typedef typename Traits::Pixel Pixel;
+    typedef typename Traits::channels_type channels_type;
+
+    const channels_type unitValue = KoColorSpaceMathsTraits<channels_type>::unitValue;
+    channels_type opacity = unitValue;
+    if (channelBytes.contains(-1)) {
+        opacity = channelBytes[-1].constData()[col];
+    }
+
+    Pixel *pixelPtr = reinterpret_cast<Pixel*>(dstPtr);
+
+    channels_type L = convertByteOrder<Traits>(reinterpret_cast<const channels_type \
*>(channelBytes[0].constData())[col]); +    channels_type a = \
convertByteOrder<Traits>(reinterpret_cast<const channels_type \
*>(channelBytes[1].constData())[col]); +    channels_type b = \
convertByteOrder<Traits>(reinterpret_cast<const channels_type \
*>(channelBytes[2].constData())[col]); +
+    pixelPtr->L = L;
+    pixelPtr->a = a;
+    pixelPtr->b = b;
+    pixelPtr->alpha = opacity;
+}
+
+void readRgbPixelCommon(int channelSize,
+                               const QMap<quint16, QByteArray> &channelBytes,
+                               int col, quint8 *dstPtr)
+{
+    if (channelSize == 1) {
+        readRgbPixel<KoBgrU8Traits>(channelBytes, col, dstPtr);
+    } else if (channelSize == 2) {
+        readRgbPixel<KoBgrU16Traits>(channelBytes, col, dstPtr);
+    } else if (channelSize == 4) {
+        readRgbPixel<KoBgrU16Traits>(channelBytes, col, dstPtr);
+    }
+}
+
+void readGrayPixelCommon(int channelSize,
+                                const QMap<quint16, QByteArray> &channelBytes,
+                                int col, quint8 *dstPtr)
+{
+    if (channelSize == 1) {
+        readGrayPixel<KoGrayU8Traits>(channelBytes, col, dstPtr);
+    } else if (channelSize == 2) {
+        readGrayPixel<KoGrayU16Traits>(channelBytes, col, dstPtr);
+    } else if (channelSize == 4) {
+        readGrayPixel<KoGrayU32Traits>(channelBytes, col, dstPtr);
+    }
+}
+
+void readCmykPixelCommon(int channelSize,
+                                const QMap<quint16, QByteArray> &channelBytes,
+                                int col, quint8 *dstPtr)
+{
+    if (channelSize == 1) {
+        readCmykPixel<KoCmykU8Traits>(channelBytes, col, dstPtr);
+    } else if (channelSize == 2) {
+        readCmykPixel<KoCmykU16Traits>(channelBytes, col, dstPtr);
+    } else if (channelSize == 4) {
+        readCmykPixel<KoCmykF32Traits>(channelBytes, col, dstPtr);
+    }
+}
+
+void readLabPixelCommon(int channelSize,
+                                const QMap<quint16, QByteArray> &channelBytes,
+                                int col, quint8 *dstPtr)
+{
+    if (channelSize == 1) {
+        readLabPixel<KoLabU8Traits>(channelBytes, col, dstPtr);
+    } else if (channelSize == 2) {
+        readLabPixel<KoLabU16Traits>(channelBytes, col, dstPtr);
+    } else if (channelSize == 4) {
+        readLabPixel<KoLabF32Traits>(channelBytes, col, dstPtr);
+    }
+}
+
+QMap<quint16, QByteArray> fetchChannelsBytes(QIODevice *io, QVector<ChannelInfo*> \
channelInfoRecords, +                                            int row, int width, int \
channelSize) +{
+    const int uncompressedLength = width * channelSize;
+
+    QMap<quint16, QByteArray> channelBytes;
+
+    foreach(ChannelInfo *channelInfo, channelInfoRecords) {
+        // user supplied masks are ignored here
+        if (channelInfo->channelId < -1) continue;
+
+        io->seek(channelInfo->channelDataStart + channelInfo->channelOffset);
+
+        if (channelInfo->compressionType == Compression::Uncompressed) {
+            channelBytes[channelInfo->channelId] = io->read(uncompressedLength);
+            channelInfo->channelOffset += uncompressedLength;
+        }
+        else if (channelInfo->compressionType == Compression::RLE) {
+            int rleLength = channelInfo->rleRowLengths[row];
+            QByteArray compressedBytes = io->read(rleLength);
+            QByteArray uncompressedBytes = Compression::uncompress(uncompressedLength, \
compressedBytes, channelInfo->compressionType); +            \
channelBytes.insert(channelInfo->channelId, uncompressedBytes); +            \
channelInfo->channelOffset += rleLength; +        }
+        else {
+            QString error = QString("Unsupported Compression mode: \
%1").arg(channelInfo->compressionType); +            dbgFile << "ERROR: fetchChannelsBytes:" << \
error; +            throw KisAslReaderUtils::ASLParseException(error);
+        }
+    }
+
+    return channelBytes;
+}
+
+typedef boost::function<void(int, const QMap<quint16, QByteArray>&, int, quint8*)> PixelFunc;
+
+void readCommon(KisPaintDeviceSP dev,
+                QIODevice *io,
+                const QRect &layerRect,
+                QVector<ChannelInfo*> infoRecords,
+                int channelSize,
+                PixelFunc pixelFunc)
+{
+    KisOffsetKeeper keeper(io);
+
+    if (layerRect.isEmpty()) {
+        dbgFile << "Empty layer!";
+        return;
+    }
+
+    KisHLineIteratorSP it = dev->createHLineIteratorNG(layerRect.left(), layerRect.top(), \
layerRect.width()); +
+    for (int i = 0 ; i < layerRect.height(); i++) {
+        QMap<quint16, QByteArray> channelBytes;
+
+        channelBytes = fetchChannelsBytes(io, infoRecords,
+                                          i, layerRect.width(), channelSize);
+
+        for (qint64 col = 0; col < layerRect.width(); col++){
+            pixelFunc(channelSize, channelBytes, col, it->rawData());
+            it->nextPixel();
+        }
+        it->nextRow();
+    }
+}
+
+void readChannels(QIODevice *io,
+                  KisPaintDeviceSP device,
+                  psd_color_mode colorMode,
+                  int channelSize,
+                  const QRect &layerRect,
+                  QVector<ChannelInfo*> infoRecords)
+{
+    switch (colorMode) {
+    case Grayscale:
+        readCommon(device, io, layerRect, infoRecords, channelSize, &readGrayPixelCommon);
+        break;
+    case RGB:
+        readCommon(device, io, layerRect, infoRecords, channelSize, &readRgbPixelCommon);
+        break;
+    case CMYK:
+        readCommon(device, io, layerRect, infoRecords, channelSize, &readCmykPixelCommon);
+        break;
+    case Lab:
+        readCommon(device, io, layerRect, infoRecords, channelSize, &readLabPixelCommon);
+        break;
+    case Bitmap:
+    case Indexed:
+    case MultiChannel:
+    case DuoTone:
+    case UNKNOWN:
+    default:
+        QString error = QString("Unsupported color mode: %1").arg(colorMode);
+        throw KisAslReaderUtils::ASLParseException(error);
+    }
+}
 }
diff --git a/krita/plugins/formats/psd/psd_pixel_utils.h \
b/krita/plugins/formats/psd/psd_pixel_utils.h index c987926..4af352f 100644
--- a/krita/plugins/formats/psd/psd_pixel_utils.h
+++ b/krita/plugins/formats/psd/psd_pixel_utils.h
@@ -19,247 +19,24 @@
 #ifndef __PSD_PIXEL_UTILS_H
 #define __PSD_PIXEL_UTILS_H
 
-#include <QtGlobal>
-#include <QMap>
+#include <QVector>
+#include <QRect>
 
-#include <KoColorSpace.h>
-#include <KoColorSpaceMaths.h>
-#include <KoColorSpaceTraits.h>
-#include <KoCmykColorSpaceTraits.h>
-
-#include <netinet/in.h> // htonl
+#include "psd.h"
+#include "kis_types.h"
 
+class QIODevice;
+struct ChannelInfo;
 
 namespace PsdPixelUtils
 {
 
-template <class Traits>
-typename Traits::channels_type convertByteOrder(typename Traits::channels_type value);
-// default implementation is undefined for every color space should be added manually
-
-template <>
-inline quint8 convertByteOrder<KoGrayU8Traits>(quint8 value) {
-    return value;
-}
-
-template <>
-inline quint16 convertByteOrder<KoGrayU16Traits>(quint16 value) {
-    return ntohs(value);
-}
-
-template <>
-inline quint32 convertByteOrder<KoGrayU32Traits>(quint32 value) {
-    return ntohs(value);
-}
-
-template <>
-inline quint8 convertByteOrder<KoBgrU8Traits>(quint8 value) {
-    return value;
-}
-
-template <>
-inline quint16 convertByteOrder<KoBgrU16Traits>(quint16 value) {
-    return ntohs(value);
-}
-
-template <>
-inline quint32 convertByteOrder<KoBgrU32Traits>(quint32 value) {
-    return ntohs(value);
-}
-
-template <>
-inline quint8 convertByteOrder<KoCmykU8Traits>(quint8 value) {
-    return value;
-}
-
-template <>
-inline quint16 convertByteOrder<KoCmykU16Traits>(quint16 value) {
-    return ntohs(value);
-}
-
-template <>
-inline float convertByteOrder<KoCmykF32Traits>(float value) {
-    return ntohs(value);
-}
-
-template <>
-inline quint8 convertByteOrder<KoLabU8Traits>(quint8 value) {
-    return value;
-}
-
-template <>
-inline quint16 convertByteOrder<KoLabU16Traits>(quint16 value) {
-    return ntohs(value);
-}
-
-template <>
-inline float convertByteOrder<KoLabF32Traits>(float value) {
-    return ntohs(value);
-}
-
-template <class Traits>
-void readGrayPixel(const QMap<quint16, QByteArray> &channelBytes,
-                  int col, quint8 *dstPtr)
-{
-    typedef typename Traits::Pixel Pixel;
-    typedef typename Traits::channels_type channels_type;
-
-    const channels_type unitValue = KoColorSpaceMathsTraits<channels_type>::unitValue;
-    channels_type opacity = unitValue;
-    if (channelBytes.contains(-1)) {
-        opacity = channelBytes[-1].constData()[col];
-    }
-
-    Pixel *pixelPtr = reinterpret_cast<Pixel*>(dstPtr);
-
-    channels_type gray = convertByteOrder<Traits>(reinterpret_cast<const channels_type \
                *>(channelBytes[0].constData())[col]);
-
-    pixelPtr->gray = gray;
-    pixelPtr->alpha = opacity;
-}
-
-template <class Traits>
-void readRgbPixel(const QMap<quint16, QByteArray> &channelBytes,
-                  int col, quint8 *dstPtr)
-{
-    typedef typename Traits::Pixel Pixel;
-    typedef typename Traits::channels_type channels_type;
-
-    const channels_type unitValue = KoColorSpaceMathsTraits<channels_type>::unitValue;
-    channels_type opacity = unitValue;
-    if (channelBytes.contains(-1)) {
-        opacity = channelBytes[-1].constData()[col];
-    }
-
-    Pixel *pixelPtr = reinterpret_cast<Pixel*>(dstPtr);
-
-    channels_type blue = convertByteOrder<Traits>(reinterpret_cast<const channels_type \
                *>(channelBytes[2].constData())[col]);
-    channels_type green = convertByteOrder<Traits>(reinterpret_cast<const channels_type \
                *>(channelBytes[1].constData())[col]);
-    channels_type red = convertByteOrder<Traits>(reinterpret_cast<const channels_type \
                *>(channelBytes[0].constData())[col]);
-
-    pixelPtr->blue = blue;
-    pixelPtr->green = green;
-    pixelPtr->red = red;
-    pixelPtr->alpha = opacity;
-}
-
-template <class Traits>
-void readCmykPixel(const QMap<quint16, QByteArray> &channelBytes,
-                       int col, quint8 *dstPtr)
-{
-    typedef typename Traits::Pixel Pixel;
-    typedef typename Traits::channels_type channels_type;
-
-    const channels_type unitValue = KoColorSpaceMathsTraits<channels_type>::unitValue;
-    channels_type opacity = unitValue;
-    if (channelBytes.contains(-1)) {
-        opacity = channelBytes[-1].constData()[col];
-    }
-
-    Pixel *pixelPtr = reinterpret_cast<Pixel*>(dstPtr);
-
-    channels_type cyan = unitValue - convertByteOrder<Traits>(reinterpret_cast<const \
                channels_type *>(channelBytes[0].constData())[col]);
-    channels_type magenta = unitValue - convertByteOrder<Traits>(reinterpret_cast<const \
                channels_type *>(channelBytes[1].constData())[col]);
-    channels_type yellow = unitValue - convertByteOrder<Traits>(reinterpret_cast<const \
                channels_type *>(channelBytes[2].constData())[col]);
-    channels_type black = unitValue - convertByteOrder<Traits>(reinterpret_cast<const \
                channels_type *>(channelBytes[3].constData())[col]);
-
-    pixelPtr->cyan = cyan;
-    pixelPtr->magenta = magenta;
-    pixelPtr->yellow = yellow;
-    pixelPtr->black = black;
-    pixelPtr->alpha = opacity;
-}
-
-template <class Traits>
-void readLabPixel(const QMap<quint16, QByteArray> &channelBytes,
-                  int col, quint8 *dstPtr)
-{
-    typedef typename Traits::Pixel Pixel;
-    typedef typename Traits::channels_type channels_type;
-
-    const channels_type unitValue = KoColorSpaceMathsTraits<channels_type>::unitValue;
-    channels_type opacity = unitValue;
-    if (channelBytes.contains(-1)) {
-        opacity = channelBytes[-1].constData()[col];
-    }
-
-    Pixel *pixelPtr = reinterpret_cast<Pixel*>(dstPtr);
-
-    channels_type L = convertByteOrder<Traits>(reinterpret_cast<const channels_type \
                *>(channelBytes[0].constData())[col]);
-    channels_type a = convertByteOrder<Traits>(reinterpret_cast<const channels_type \
                *>(channelBytes[1].constData())[col]);
-    channels_type b = convertByteOrder<Traits>(reinterpret_cast<const channels_type \
                *>(channelBytes[2].constData())[col]);
-
-    pixelPtr->L = L;
-    pixelPtr->a = a;
-    pixelPtr->b = b;
-    pixelPtr->alpha = opacity;
-}
-
-template <class Traits8, class Traits16, class Traits32>
-inline void readPixelCommon(int channelSize,
-                            const QMap<quint16, QByteArray> &channelBytes,
-                            int col, quint8 *dstPtr)
-{
-    if (channelSize == 1) {
-        readRgbPixel<Traits8>(channelBytes, col, dstPtr);
-    } else if (channelSize == 2) {
-        readRgbPixel<Traits16>(channelBytes, col, dstPtr);
-    } else if (channelSize == 4) {
-        readRgbPixel<Traits32>(channelBytes, col, dstPtr);
-    }
-}
-
-inline void readRgbPixelCommon(int channelSize,
-                               const QMap<quint16, QByteArray> &channelBytes,
-                               int col, quint8 *dstPtr)
-{
-    if (channelSize == 1) {
-        readRgbPixel<KoBgrU8Traits>(channelBytes, col, dstPtr);
-    } else if (channelSize == 2) {
-        readRgbPixel<KoBgrU16Traits>(channelBytes, col, dstPtr);
-    } else if (channelSize == 4) {
-        readRgbPixel<KoBgrU16Traits>(channelBytes, col, dstPtr);
-    }
-}
-
-inline void readGrayPixelCommon(int channelSize,
-                                const QMap<quint16, QByteArray> &channelBytes,
-                                int col, quint8 *dstPtr)
-{
-    if (channelSize == 1) {
-        readGrayPixel<KoGrayU8Traits>(channelBytes, col, dstPtr);
-    } else if (channelSize == 2) {
-        readGrayPixel<KoGrayU16Traits>(channelBytes, col, dstPtr);
-    } else if (channelSize == 4) {
-        readGrayPixel<KoGrayU32Traits>(channelBytes, col, dstPtr);
-    }
-}
-
-inline void readCmykPixelCommon(int channelSize,
-                                const QMap<quint16, QByteArray> &channelBytes,
-                                int col, quint8 *dstPtr)
-{
-    if (channelSize == 1) {
-        readCmykPixel<KoCmykU8Traits>(channelBytes, col, dstPtr);
-    } else if (channelSize == 2) {
-        readCmykPixel<KoCmykU16Traits>(channelBytes, col, dstPtr);
-    } else if (channelSize == 4) {
-        readCmykPixel<KoCmykF32Traits>(channelBytes, col, dstPtr);
-    }
-}
-
-inline void readLabPixelCommon(int channelSize,
-                                const QMap<quint16, QByteArray> &channelBytes,
-                                int col, quint8 *dstPtr)
-{
-    if (channelSize == 1) {
-        readLabPixel<KoLabU8Traits>(channelBytes, col, dstPtr);
-    } else if (channelSize == 2) {
-        readLabPixel<KoLabU16Traits>(channelBytes, col, dstPtr);
-    } else if (channelSize == 4) {
-        readLabPixel<KoLabF32Traits>(channelBytes, col, dstPtr);
-    }
-}
+    void readChannels(QIODevice *io,
+                      KisPaintDeviceSP device,
+                      psd_color_mode colorMode,
+                      int channelSize,
+                      const QRect &layerRect,
+                      QVector<ChannelInfo*> infoRecords);
 
 }
 


[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic