// // Copyright (c) 2014 VK.com // // Permission is hereby granted, free of charge, to any person obtaining a copy of // this software and associated documentation files (the "Software"), to deal in // the Software without restriction, including without limitation the rights to // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of // the Software, and to permit persons to whom the Software is furnished to do so, // subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // /** * PhotoSize.java * VK Dev * * Created by Babichev Vitaly on 03.10.13. * Copyright (c) 2013 VK. All rights reserved. */ package com.vk.sdk.api.model; import android.os.Parcel; import android.os.Parcelable; import android.text.TextUtils; import org.json.JSONException; import org.json.JSONObject; /** * Describes an photo info in <a href="http://vk.com/dev/photo_sizes">special format<a/>. * * Some methods returns information about copies of the original image in different sizes, * Represented as an array of sizes, containing a description of the objects of this class. * * <b>Sizes value example:</b> * * Original photo — https://pp.vk.me/c323930/v323930021/53fb/1VrEC2eSkZQ.jpg,1280x856px, * "width/height" ratio is 1.495327102803738 * * <code> sizes: [{ src: http://cs323930.vk.me/v323930021/53f7/OwV0l2YFJ7s.jpg width: 75, height: 50, type: 's' }, { src: http://cs323930.vk.me/v323930021/53f8/qX8MRNyUPqg.jpg, width: 130, height: 87, type: 'm' }, { src: http://cs323930.vk.me/v323930021/53f9/7fBJyr9OHMA.jpg, width: 604, height: 404, type: 'x' }, { src: http://cs323930.vk.me/v323930021/53fa/bskHpsuH6sM.jpg, width: 807, height: 540, type: 'y' }, { src: http://cs323930.vk.me/v323930021/53fb/1VrEC2eSkZQ.jpg, width: 1280, height: 856, type: 'z' }, { src: http://cs323930.vk.me/v323930021/53fc/iAl-TIHfRDY.jpg, width: 130, height: 87, type: 'o' }, { src: http://cs323930.vk.me/v323930021/53fd/qjD0fbHkgmI.jpg, width: 200, height: 134, type: 'p' }, { src: http://cs323930.vk.me/v323930021/53fe/3d2nCvvKQfw.jpg, width: 320, height: 214, type: 'q' }, { src: http://cs323930.vk.me/v323930021/53ff/uK_Nj34SIY8.jpg, width: 510, height: 341, type: 'r' }] * </code> * */ public class VKApiPhotoSize extends VKApiModel implements Comparable<VKApiPhotoSize>, Parcelable, Identifiable { /** * Proportional copy with 75px max width */ public final static char S = 's'; /** * Proportional copy with 130px max width */ public final static char M = 'm'; /** * Proportional copy with 604px max width */ public final static char X = 'x'; /** * Proportional copy with 807px max width */ public final static char Y = 'y'; /** * If original image's "width/height" ratio is less or equal to 3:2, then proportional * copy with 130px max width. If original image's "width/height" ratio is more than 3:2, * then copy of cropped by left side image with 130px max width and 3:2 sides ratio. */ public final static char O = 'o'; /** * If original image's "width/height" ratio is less or equal to 3:2, then proportional * copy with 200px max width. If original image's "width/height" ratio is more than 3:2, * then copy of cropped by left side image with 200px max width and 3:2 sides ratio. */ public final static char P = 'p'; /** * If original image's "width/height" ratio is less or equal to 3:2, then proportional * copy with 320px max width. If original image's "width/height" ratio is more than 3:2, * then copy of cropped by left side image with 320px max width and 3:2 sides ratio. */ public final static char Q = 'q'; /** * Proportional copy with 1280x1024px max size */ public final static char Z = 'z'; /** * Proportional copy with 2560x2048px max size */ public final static char W = 'w'; /** * Url of image */ public String src; /** * Width of image in pixels */ public int width; /** * Height of image in pixels */ public int height; /** * Designation of size and proportions copy, @see {{@link #S}, {@link #M}, {@link #X}, {@link #O}, {@link #P}, {@link #Q}, {@link #Y}, {@link #Z}, {@link #W}} */ public char type; private VKApiPhotoSize() { } private VKApiPhotoSize(Parcel in) { this.src = in.readString(); this.width = in.readInt(); this.height = in.readInt(); this.type = (char) in.readInt(); } @Override public int compareTo(VKApiPhotoSize another) { // Так как основной превалирующий элемент в фотографиях именно ширина и все фотографии пропорциональны, // то сравниваем именно по ней return this.width < another.width ? -1 : (this.width == another.width ? 0 : 1); } @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeString(this.src); dest.writeInt(this.width); dest.writeInt(this.height); dest.writeInt((int)this.type); } @Override public int getId() { return 0; } public static Creator<VKApiPhotoSize> CREATOR = new Creator<VKApiPhotoSize>() { public VKApiPhotoSize createFromParcel(Parcel source) { return new VKApiPhotoSize(source); } public VKApiPhotoSize[] newArray(int size) { return new VKApiPhotoSize[size]; } }; public VKApiPhotoSize(JSONObject from) throws JSONException { parse(from, 0, 0); } /** * Creates dimension from {@code source}. Used in parsing. * If size is not specified copies calculates them based on internal algorithms. * @param source object in format, returned VK API, which is generated from the dimension * @param originalWidth original image width in pixels * @param originalHeight original image height in pixels */ public static VKApiPhotoSize parse(JSONObject source, int originalWidth, int originalHeight) { VKApiPhotoSize result = new VKApiPhotoSize(); result.src = source.optString("src"); result.width = source.optInt("width"); result.height = source.optInt("height"); String type = source.optString("type"); if(!TextUtils.isEmpty(type)) { result.type = type.charAt(0); } // Казалось бы, теперь можно с чистой советью закончить метод. // Но нет, оказывается, width и height не просчитывается на некоторых серверах ВК. // Приходится гадать на кофейной гуще. if(result.width == 0 || result.height == 0) { fillDimensions(result, originalWidth, originalHeight); } return result; } /* * Устанавливает размерность исходя из размеров оригинала и типа изображения. */ private static void fillDimensions(VKApiPhotoSize result, int originalWidth, int originalHeight) { float ratio = (float) originalWidth / originalHeight; switch (result.type) { case S: { fillDimensionSMXY(result, ratio, Math.min(originalWidth, 75)); } break; case M: { fillDimensionSMXY(result, ratio, Math.min(originalWidth, 130)); } break; case X: { fillDimensionSMXY(result, ratio, Math.min(originalWidth, 604)); } break; case Y: { fillDimensionSMXY(result, ratio, Math.min(originalWidth, 807)); } break; case O: { fillDimensionOPQ(result, ratio, Math.min(originalWidth, 130)); } break; case P: { fillDimensionOPQ(result, ratio, Math.min(originalWidth, 200)); } break; case Q: { fillDimensionOPQ(result, ratio, Math.min(originalWidth, 320)); } break; case Z: { fillDimensionZW(result, ratio, Math.min(originalWidth, 1280), Math.min(originalHeight, 1024)); } break; case W: { fillDimensionZW(result, ratio, Math.min(originalWidth, 2560), Math.min(originalHeight, 2048)); } break; } } /* * Про S, M, X, Y известно, про копия обязательно пропорциональна, а ширина не должна превышать заданную. * Это значит, что для всех случаев(кроме тех, когда ширина картинки меньше указанной) соотношения * сторон картинка впишется пропорционально по ширине. */ private static void fillDimensionSMXY(VKApiPhotoSize result, float ratio, int width) { result.width = width; result.height = (int) Math.ceil(result.width / ratio); } /* * Пропорциональная ширина. В принципе, все, что было сказано к предыдущему, верно и здесь, * за исключением того, что высота здесь не может превышать ширину * 1,5f */ private static void fillDimensionOPQ(VKApiPhotoSize result, float ratio, int width) { fillDimensionSMXY(result, Math.min(1.5f, ratio), width); } /* * А здесь просто берем одну сторону за фактическую и исходя из нее вычисляем другую. */ private static void fillDimensionZW(VKApiPhotoSize result, float ratio, int allowedWidth, int allowedHeight) { if(ratio > 1) { // ширина больше высоты result.width = allowedWidth; result.height = (int) (result.width / ratio); } else { result.height = allowedHeight; result.width = (int) (result.height * ratio); } } /** * Creates a dimension with explicit dimensions. * Can be helpful if the dimensions are exactly known. */ public static VKApiPhotoSize create(String url, int width, int height) { VKApiPhotoSize result = new VKApiPhotoSize(); result.src = url; result.width = width; result.height = height; float ratio = width / (float) height ; if(width <= 75) { result.type = S; } else if(width <= 130) { result.type = ratio <= 1.5f ? O : M; } else if(width <= 200 && ratio <= 1.5f) { result.type = P; } else if(width <= 320 && ratio <= 1.5f) { result.type = Q; } else if(width <= 604 ) { result.type = X; } else if(width <= 807) { result.type = Y; } else if(width <= 1280 && height <= 1024) { result.type = Z; } else if(width <= 2560 && height <= 2048) { result.type = W; } return result; } /** * Creates a dimension type and size of the original. */ public static VKApiPhotoSize create(String url, char type, int originalWidth, int originalHeight) { VKApiPhotoSize result = new VKApiPhotoSize(); result.src = url; result.type = type; fillDimensions(result, originalWidth, originalHeight); return result; } /** * Creates a square dimension type and size of the original. */ public static VKApiPhotoSize create(String url, int dimension) { return create(url, dimension, dimension); } }